diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-08 00:22:18 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-08 00:22:18 +0800 |
| commit | bc655637c934aeadfad1814914cc5a8aff1c5db5 (patch) | |
| tree | 98b81c02c1fce1968a94f4913d9dd52e01cb46b8 | |
| parent | 3b4193027bd4dff95b65eaab57a10e793d68469e (diff) | |
konwinlib: event queue on X11 and Emscripten, text events, live callback
| -rw-r--r-- | kon/konwinlib.h | 344 |
1 files changed, 200 insertions, 144 deletions
diff --git a/kon/konwinlib.h b/kon/konwinlib.h index c50f828..29a563b 100644 --- a/kon/konwinlib.h +++ b/kon/konwinlib.h @@ -57,7 +57,8 @@ typedef enum kon_eventType { KON_EVENT_KEY_UP, KON_EVENT_MOUSE_DOWN, KON_EVENT_MOUSE_UP, - KON_EVENT_MOUSE_MOVE + KON_EVENT_MOUSE_MOVE, + KON_EVENT_TEXT } kon_eventType_t; typedef enum kon_mouseButton { @@ -101,6 +102,7 @@ typedef struct kon_event { kon_eventType_t type; int width, height; /* will be used to store data for resize events */ int key; + unsigned int codepoint; /* unicode codepoint, only set for KON_EVENT_TEXT */ int mouseX, mouseY; kon_mouseButton_t mouseButton; } kon_event_t; @@ -125,6 +127,9 @@ void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int typedef void (*kon_mainLoopFn)(void *userData); void kon_runMainLoop(kon_window_t *window, kon_mainLoopFn callback, void *userData); +/* Windows stops your loop while the window is being dragged or resized, this keeps calling callback meanwhile */ +void kon_setLiveCallback(kon_window_t *window, kon_mainLoopFn callback, void *userData); + /*** implementation ***/ #ifdef KONWINLIB_IMPLEMENTATION @@ -175,18 +180,7 @@ struct kon_window { char canvasID[64]; bool shouldClose; int exitKey; - - bool hasKeyEvent; - kon_eventType_t keyEventType; - int keyEventKey; - - bool hasResizeEvent; - int resizeWidth, resizeHeight; - - bool hasMouseEvent; - kon_eventType_t mouseEventType; - int mouseEventX, mouseEventY; - kon_mouseButton_t mouseEventButton; + kon_eventQueue_t queue; }; static kon_key_t kon_translateEmKey_(int keyCode) { @@ -292,13 +286,16 @@ void kon_deinit(void) { static EM_BOOL kon_keyDownCallback_(int eventType, const EmscriptenKeyboardEvent *e, void *userData) { (void)eventType; kon_window_t *window = (kon_window_t *)userData; + kon_event_t ev = {0}; - window->hasKeyEvent = true; - window->keyEventType = KON_EVENT_KEY_DOWN; - window->keyEventKey = kon_translateEmKey_(e->keyCode); + ev.type = KON_EVENT_KEY_DOWN; + ev.key = (int)kon_translateEmKey_((int)e->keyCode); + kon_pushEvent_(&window->queue, &ev); - if (window->exitKey != 0 && window->keyEventKey == window->exitKey) { + if (window->exitKey != 0 && ev.key == window->exitKey) { window->shouldClose = true; + ev.type = KON_EVENT_CLOSE; + kon_pushEvent_(&window->queue, &ev); } return EM_TRUE; @@ -307,52 +304,44 @@ static EM_BOOL kon_keyDownCallback_(int eventType, const EmscriptenKeyboardEvent static EM_BOOL kon_keyUpCallback_(int eventType, const EmscriptenKeyboardEvent *e, void *userData) { (void)eventType; kon_window_t *window = (kon_window_t *)userData; + kon_event_t ev = {0}; - window->hasKeyEvent = true; - window->keyEventType = KON_EVENT_KEY_UP; - window->keyEventKey = kon_translateEmKey_(e->keyCode); + ev.type = KON_EVENT_KEY_UP; + ev.key = (int)kon_translateEmKey_((int)e->keyCode); + kon_pushEvent_(&window->queue, &ev); return EM_TRUE; } -static EM_BOOL kon_mouseDownCallback_(int eventType, const EmscriptenMouseEvent *e, void *userData) { +static EM_BOOL kon_keyPressCallback_(int eventType, const EmscriptenKeyboardEvent *e, void *userData) { (void)eventType; kon_window_t *window = (kon_window_t *)userData; + kon_event_t ev = {0}; - if (e->button > KON_MOUSE_RIGHT) return EM_FALSE; + if (e->charCode < 32 || e->charCode == 127) return EM_FALSE; - window->hasMouseEvent = true; - window->mouseEventType = KON_EVENT_MOUSE_DOWN; - window->mouseEventButton = (kon_windowButton_t)e->button; - window->mouseEventX = e->targetX; - window->mouseEventY = e->targetY; + ev.type = KON_EVENT_TEXT; + ev.codepoint = (unsigned int)e->charCode; + kon_pushEvent_(&window->queue, &ev); return EM_TRUE; } -static EM_BOOL kon_mouseUpCallback_(int eventType, const EmscriptenMouseEvent *e, void *userData) { - (void)eventType; +static EM_BOOL kon_mouseCallback_(int eventType, const EmscriptenMouseEvent *e, void *userData) { kon_window_t *window = (kon_window_t *)userData; + kon_event_t ev = {0}; - if (e->button > KON_MOUSE_RIGHT) return EM_FALSE: - - window->hasMouseEvent = true; - window->mouseEventType = KON_EVENT_MOUSE_UP; - window->mouseEventButton = (kon_windowButton_t)e->button; - window->mouseEventX = e->targetX; - window->mouseEventY = e->targetY; - - return EM_TRUE; -} - -static EM_BOOL kon_mouseMoveCallback_(int eventType, const EmscriptenMouseEvent *e, void *userData) { - (void)eventType; - kon_window_t *window = (kon_window_t *)userData; + if (eventType == EMSCRIPTEN_EVENT_MOUSEMOVE) { + ev.type = KON_EVENT_MOUSE_MOVE; + } else { + if (e->button > KON_MOUSE_RIGHT) return EM_FALSE; + ev.type = (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) ? KON_EVENT_MOUSE_DOWN : KON_EVENT_MOUSE_UP; + ev.mouseButton = (kon_mouseButton_t)e->button; + } - window->hasMouseEvent = true; - window->mouseEventType = KON_EVENT_MOUSE_MOVE; - window->mouseEventX = e->targetX; - window->mouseEventY = e->targetY; + ev.mouseX = (int)e->targetX; + ev.mouseY = (int)e->targetY; + kon_pushEvent_(&window->queue, &ev); return EM_TRUE; } @@ -365,18 +354,12 @@ kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int h return NULL; } - kon_window_t *window = malloc(sizeof(kon_window_t)); + kon_window_t *window = calloc(1, sizeof(kon_window_t)); if (!window) return NULL; strncpy(window->canvasID, "#canvas", sizeof(window->canvasID) - 1); window->canvasID[sizeof(window->canvasID) - 1] = '\0'; - window->shouldClose = false; - window->exitKey = 0; - window->hasKeyEvent = false; - window->hasResizeEvent = false; - window->hasMouseEvent = false; - emscripten_set_canvas_element_size(window->canvasID, width, height); EM_ASM({ @@ -385,9 +368,10 @@ kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int h emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, window, EM_FALSE, kon_keyDownCallback_); emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, window, EM_FALSE, kon_keyUpCallback_); - emscripten_set_mousedown_callback(window->canvasID, window, EM_FALSE, kon_mouseDownCallback_); - emscripten_set_mouseup_callback(window->canvasID, window, EM_FALSE, kon_mouseUpCallback_); - emscripten_set_mousemove_callback(window->canvasID, window, EM_FALSE, kon_mouseMoveCallback_); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, window, EM_FALSE, kon_keyPressCallback_); + emscripten_set_mousedown_callback(window->canvasID, window, EM_FALSE, kon_mouseCallback_); + emscripten_set_mouseup_callback(window->canvasID, window, EM_FALSE, kon_mouseCallback_); + emscripten_set_mousemove_callback(window->canvasID, window, EM_FALSE, kon_mouseCallback_); return window; } @@ -397,6 +381,7 @@ void kon_destroyWindow(kon_window_t *window) { emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, NULL); emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, NULL); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, NULL); emscripten_set_mousedown_callback(window->canvasID, NULL, EM_FALSE, NULL); emscripten_set_mouseup_callback(window->canvasID, NULL, EM_FALSE, NULL); emscripten_set_mousemove_callback(window->canvasID, NULL, EM_FALSE, NULL); @@ -441,33 +426,11 @@ void kon_getWindowSize(kon_window_t *window, int *width, int *height) { int kon_pollEvent(kon_window_t *window, kon_event_t *event) { if (!window || !event) return 0; - event->type = KON_EVENT_NONE; - - if (window->hasResizeEvent) { - event->type = KON_EVENT_RESIZE; - event->width = window->resizeWidth; - event->height = window->resizeHeight; - window->hasResizeEvent = false; - return 1; - } - - if (window->hasKeyEvent) { - event->type = window->keyEventType; - event->key = window->keyEventKey; - window->hasKeyEvent = false; - return 1; - } - - if (window->hasMouseEvent) { - event->type = window->mouseEventType; - event->mouseX = window->mouseEventX; - event->mouseY = window-<mouseEventY; - event->mouseButton = window->mouseEventButton; - window->hasMouseEvent = false; - return 1; + if (!kon_popEvent_(&window->queue, event)) { + event->type = KON_EVENT_NONE; + return 0; } - - return 0; + return 1; } EM_JS(void, kon_jsBlit_, (const char *canvasID, uint32_t *pixels, int width, int height), { @@ -605,7 +568,10 @@ bool kon_init(void) { kon_context_t *ctx = malloc(sizeof(kon_context_t)); if (!ctx) return false; ctx->display = XOpenDisplay(NULL); - if (!ctx->display) return false; + if (!ctx->display) { + free(ctx); + return false; + } kon_ctx = ctx; return true; } @@ -628,6 +594,8 @@ struct kon_window { Atom wm_delete; bool shouldClose; int exitKey; + int width, height; + kon_eventQueue_t queue; }; kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int height, kon_windowFlags_t flags) { @@ -636,7 +604,7 @@ kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int h return NULL; } - kon_window_t *window = malloc(sizeof(kon_window_t)); + kon_window_t *window = calloc(1, sizeof(kon_window_t)); if (!window) return NULL; int screen = DefaultScreen(kon_ctx->display); @@ -763,8 +731,9 @@ kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int h XFlush(kon_ctx->display); window->exitKey = 0; - window->shouldClose = false; + window->width = width; + window->height = height; return window; } @@ -818,77 +787,109 @@ void kon_getWindowSize(kon_window_t *window, int *width, int *height) { *height = attrs.height; } -int kon_pollEvent(kon_window_t *window, kon_event_t *event) { - if (!window || !kon_ctx || !event) return 0; +static void kon_pumpX11_(kon_window_t *window) { + while (XPending(kon_ctx->display)) { + XEvent xev; + kon_event_t ev = {0}; - event->type = KON_EVENT_NONE; + XNextEvent(kon_ctx->display, &xev); - if (!XPending(kon_ctx->display)) return 0; + switch (xev.type) { + case ClientMessage: + if ((Atom)xev.xclient.data.l[0] == window->wm_delete) { + ev.type = KON_EVENT_CLOSE; + window->shouldClose = true; + kon_pushEvent_(&window->queue, &ev); + } + break; - XEvent xev; - XNextEvent(kon_ctx->display, &xev); + case ConfigureNotify: + /* moving the window also sends this, only report real size changes */ + if (xev.xconfigure.width != window->width || xev.xconfigure.height != window->height) { + window->width = xev.xconfigure.width; + window->height = xev.xconfigure.height; + ev.type = KON_EVENT_RESIZE; + ev.width = window->width; + ev.height = window->height; + kon_pushEvent_(&window->queue, &ev); + } + break; - if (xev.type == ClientMessage) { - if ((Atom)xev.xclient.data.l[0] == window->wm_delete) { - event->type = KON_EVENT_CLOSE; - window->shouldClose = true; - } - } + case KeyPress: { + char text[8]; + int len; - if (xev.type == ConfigureNotify) { - event->type = KON_EVENT_RESIZE; - event->width = xev.xconfigure.width; - event->height = xev.xconfigure.height; - return 1; - } + ev.type = KON_EVENT_KEY_DOWN; + ev.key = (int)kon_translateX11Key_(XLookupKeysym(&xev.xkey, 0)); + kon_pushEvent_(&window->queue, &ev); - if (xev.type == KeyPress) { - event->type = KON_EVENT_KEY_DOWN; - event->key = (int)kon_translateX11Key_(XLookupKeysym(&xev.xkey, 0)); - if (window->exitKey != 0 && event->key == window->exitKey) { - window->shouldClose = true; - } - return 1; - } + if (window->exitKey != 0 && ev.key == window->exitKey) { + window->shouldClose = true; + ev.type = KON_EVENT_CLOSE; + kon_pushEvent_(&window->queue, &ev); + } - if (xev.type == KeyRelease) { - event->type = KON_EVENT_KEY_UP; - event->key = (int)kon_translateX11Key_(XLookupKeysym(&xev.xkey, 0)); - return 1; - } + len = XLookupString(&xev.xkey, text, (int)sizeof(text), NULL, NULL); + if (len == 1 && (unsigned char)text[0] >= 32 && (unsigned char)text[0] != 127 && + !(xev.xkey.state & (ControlMask | Mod1Mask))) { + ev.type = KON_EVENT_TEXT; + ev.key = 0; + ev.codepoint = (unsigned char)text[0]; + kon_pushEvent_(&window->queue, &ev); + } + break; + } - if (xev.type == ButtonPress || xev.type == ButtonRelease) { - int konButton = -1; - switch (xev.xbutton.button) { - case Button1: - konButton = KON_MOUSE_LEFT; + case KeyRelease: + /* X reports key repeat as a release followed by a press with the same time, drop the release */ + if (XPending(kon_ctx->display)) { + XEvent next; + XPeekEvent(kon_ctx->display, &next); + if (next.type == KeyPress && next.xkey.time == xev.xkey.time && next.xkey.keycode == xev.xkey.keycode) { + break; + } + } + ev.type = KON_EVENT_KEY_UP; + ev.key = (int)kon_translateX11Key_(XLookupKeysym(&xev.xkey, 0)); + kon_pushEvent_(&window->queue, &ev); break; - case Button2: - konButton = KON_MOUSE_MIDDLE; + + case ButtonPress: + case ButtonRelease: + switch (xev.xbutton.button) { + case Button1: ev.mouseButton = KON_MOUSE_LEFT; break; + case Button2: ev.mouseButton = KON_MOUSE_MIDDLE; break; + case Button3: ev.mouseButton = KON_MOUSE_RIGHT; break; + default: continue; + } + ev.type = (xev.type == ButtonPress) ? KON_EVENT_MOUSE_DOWN : KON_EVENT_MOUSE_UP; + ev.mouseX = xev.xbutton.x; + ev.mouseY = xev.xbutton.y; + kon_pushEvent_(&window->queue, &ev); break; - case Button3: - konButton = KON_MOUSE_RIGHT; + + case MotionNotify: + ev.type = KON_EVENT_MOUSE_MOVE; + ev.mouseX = xev.xmotion.x; + ev.mouseY = xev.xmotion.y; + kon_pushEvent_(&window->queue, &ev); break; + default: break; } - - if (konButton != -1) { - event->type = (xev.type == ButtonPress) ? KON_EVENT_MOUSE_DOWN : KON_EVENT_MOUSE_UP; - event->mouseButton = (kon_mouseButton_t)konButton; - event->mouseX = xev.xbutton.x; - event->mouseY = xev.xbutton.y; - } - return 1; } +} - if (xev.type == MotionNotify) { - event->type = KON_EVENT_MOUSE_MOVE; - event->mouseX = xev.xmotion.x; - event->mouseY = xev.xmotion.y; - return 1; - } +int kon_pollEvent(kon_window_t *window, kon_event_t *event) { + if (!window || !kon_ctx || !event) return 0; + + kon_pumpX11_(window); + if (!kon_popEvent_(&window->queue, event)) { + event->type = KON_EVENT_NONE; + return 0; + } return 1; } @@ -929,6 +930,10 @@ struct kon_window { bool shouldClose; bool isTransparent; int exitKey; + unsigned short highSurrogate; + kon_mainLoopFn liveFn; + void *liveData; + bool inLive; kon_eventQueue_t queue; }; @@ -1055,6 +1060,26 @@ LRESULT CALLBACK kon_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) kon_pushEvent_(&window->queue, &ev); } break; + case WM_CHAR: + if (window) { + unsigned int c = (unsigned int)wParam; + kon_event_t ev = {0}; + + if (c >= 0xD800 && c <= 0xDBFF) { + window->highSurrogate = (unsigned short)c; + break; + } + if (c >= 0xDC00 && c <= 0xDFFF) { + c = 0x10000 + (((unsigned int)window->highSurrogate - 0xD800) << 10) + (c - 0xDC00); + window->highSurrogate = 0; + } + if (c < 32 || c == 127) break; + + ev.type = KON_EVENT_TEXT; + ev.codepoint = c; + kon_pushEvent_(&window->queue, &ev); + } + break; case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: @@ -1097,6 +1122,19 @@ LRESULT CALLBACK kon_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) kon_pushEvent_(&window->queue, &ev); } break; + case WM_ENTERSIZEMOVE: + if (window && window->liveFn) SetTimer(hwnd, 1, 8, NULL); + break; + case WM_EXITSIZEMOVE: + KillTimer(hwnd, 1); + break; + case WM_TIMER: + if (window && wParam == 1 && window->liveFn && !window->inLive) { + window->inLive = true; + window->liveFn(window->liveData); + window->inLive = false; + } + return 0; case WM_DESTROY: PostQuitMessage(0); return 0; @@ -1249,10 +1287,12 @@ void kon_getWindowSize(kon_window_t *window, int *width, int *height) { int kon_pollEvent(kon_window_t *window, kon_event_t *event) { if (!window || !event) return 0; - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); + if (!window->inLive) { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } } if (!kon_popEvent_(&window->queue, event)) { @@ -1262,6 +1302,12 @@ int kon_pollEvent(kon_window_t *window, kon_event_t *event) { return 1; } +void kon_setLiveCallback(kon_window_t *window, kon_mainLoopFn callback, void *userData) { + if (!window) return; + window->liveFn = callback; + window->liveData = userData; +} + void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int height) { if (!window || !pixels) return; @@ -1309,6 +1355,14 @@ void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int #error "konwinlib.h: unsupported platform" #endif /* end of platform switch */ +#ifndef _WIN32 + +void kon_setLiveCallback(kon_window_t *window, kon_mainLoopFn callback, void *userData) { + (void)window; (void)callback; (void)userData; +} + +#endif + /*** main loop ***/ #ifdef __EMSCRIPTEN__ @@ -1345,6 +1399,8 @@ void kon_runMainLoop(kon_window_t *window, kon_mainLoopFn callback, void *userDa #else void kon_runMainLoop(kon_window_t *window, kon_mainLoopFn callback, void *userData) { + kon_setLiveCallback(window, callback, userData); + while (!kon_windowShouldClose(window)) { callback(userData); } |
