Selaa lähdekoodia

Merge pull request #786 from rswinkle/fix_glfw_repeat_behavior

Fix glfw repeat behavior
Rob Loach 7 kuukautta sitten
vanhempi
commit
76b10f07c4

+ 0 - 1
demo/glfw_opengl2/main.c

@@ -20,7 +20,6 @@
 #define NK_INCLUDE_DEFAULT_FONT
 #define NK_IMPLEMENTATION
 #define NK_GLFW_GL2_IMPLEMENTATION
-#define NK_KEYSTATE_BASED_INPUT
 #include "../../nuklear.h"
 #include "nuklear_glfw_gl2.h"
 

+ 81 - 21
demo/glfw_opengl2/nuklear_glfw_gl2.h

@@ -28,7 +28,9 @@ NK_API void                 nk_glfw3_render(enum nk_anti_aliasing);
 NK_API void                 nk_glfw3_shutdown(void);
 
 NK_API void                 nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
+NK_API void                 nk_glfw3_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods);
 NK_API void                 nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff);
+NK_API void                 nk_glfw3_mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
 
 #endif
 
@@ -74,6 +76,7 @@ static struct nk_glfw {
     struct nk_font_atlas atlas;
     struct nk_vec2 fb_scale;
     unsigned int text[NK_GLFW_TEXT_MAX];
+    nk_char key_events[NK_KEY_MAX];
     int text_len;
     struct nk_vec2 scroll;
     double last_button_click;
@@ -209,6 +212,56 @@ nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint)
         glfw.text[glfw.text_len++] = codepoint;
 }
 
+NK_API void
+nk_glfw3_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods)
+{
+    /*
+     * convert GLFW_REPEAT to down (technically GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT are
+     * already 0, 1, 2 but just to be clearer)
+     */
+    nk_char a = (action == GLFW_RELEASE) ? nk_false : nk_true;
+
+    NK_UNUSED(win);
+    NK_UNUSED(scancode);
+    NK_UNUSED(mods);
+
+    switch (key) {
+    case GLFW_KEY_DELETE:    glfw.key_events[NK_KEY_DEL] = a; break;
+    case GLFW_KEY_TAB:       glfw.key_events[NK_KEY_TAB] = a; break;
+    case GLFW_KEY_BACKSPACE: glfw.key_events[NK_KEY_BACKSPACE] = a; break;
+    case GLFW_KEY_UP:        glfw.key_events[NK_KEY_UP] = a; break;
+    case GLFW_KEY_DOWN:      glfw.key_events[NK_KEY_DOWN] = a; break;
+    case GLFW_KEY_LEFT:      glfw.key_events[NK_KEY_LEFT] = a; break;
+    case GLFW_KEY_RIGHT:     glfw.key_events[NK_KEY_RIGHT] = a; break;
+
+    case GLFW_KEY_PAGE_UP:   glfw.key_events[NK_KEY_SCROLL_UP] = a; break;
+    case GLFW_KEY_PAGE_DOWN: glfw.key_events[NK_KEY_SCROLL_DOWN] = a; break;
+
+    /* have to add all keys used for nuklear to get correct repeat behavior
+     * NOTE these are scancodes so your custom layout won't matter unfortunately
+     * Also while including everything will prevent unnecessary input calls,
+     * only the ones with visible effects really matter, ie paste, undo, redo
+     * selecting all, copying or cutting 40 times before you release the keys
+     * doesn't actually cause any visible problems */
+
+    case GLFW_KEY_C:         glfw.key_events[NK_KEY_COPY] = a; break;
+    case GLFW_KEY_V:         glfw.key_events[NK_KEY_PASTE] = a; break;
+    case GLFW_KEY_X:         glfw.key_events[NK_KEY_CUT] = a; break;
+    case GLFW_KEY_Z:         glfw.key_events[NK_KEY_TEXT_UNDO] = a; break;
+    case GLFW_KEY_R:         glfw.key_events[NK_KEY_TEXT_REDO] = a; break;
+    case GLFW_KEY_B:         glfw.key_events[NK_KEY_TEXT_LINE_START] = a; break;
+    case GLFW_KEY_E:         glfw.key_events[NK_KEY_TEXT_LINE_END] = a; break;
+    case GLFW_KEY_A:         glfw.key_events[NK_KEY_TEXT_SELECT_ALL] = a; break;
+
+    case GLFW_KEY_ENTER:
+    case GLFW_KEY_KP_ENTER:
+        glfw.key_events[NK_KEY_ENTER] = a;
+        break;
+    default:
+        ;
+    }
+}
+
 NK_API void
 nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff)
 {
@@ -263,6 +316,7 @@ nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state)
     if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
         glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
         glfwSetCharCallback(win, nk_glfw3_char_callback);
+        glfwSetKeyCallback(win, nk_glfw3_key_callback);
         glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
     }
     nk_init_default(&glfw.ctx, 0);
@@ -305,6 +359,7 @@ nk_glfw3_new_frame(void)
     double x, y;
     struct nk_context *ctx = &glfw.ctx;
     struct GLFWwindow *win = glfw.win;
+    nk_char* k_state = glfw.key_events;
 
     /* update the timer */
     float delta_time_now = (float)glfwGetTime();
@@ -326,38 +381,39 @@ nk_glfw3_new_frame(void)
     else if (ctx->input.mouse.ungrab)
         glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
 
-    nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS ||
-                                    glfwGetKey(win, GLFW_KEY_KP_ENTER) == GLFW_PRESS);
+    if (k_state[NK_KEY_DEL] >= 0) nk_input_key(ctx, NK_KEY_DEL, k_state[NK_KEY_DEL]);
+    if (k_state[NK_KEY_ENTER] >= 0) nk_input_key(ctx, NK_KEY_ENTER, k_state[NK_KEY_ENTER]);
+
+    if (k_state[NK_KEY_TAB] >= 0) nk_input_key(ctx, NK_KEY_TAB, k_state[NK_KEY_TAB]);
+    if (k_state[NK_KEY_BACKSPACE] >= 0) nk_input_key(ctx, NK_KEY_BACKSPACE, k_state[NK_KEY_BACKSPACE]);
+    if (k_state[NK_KEY_UP] >= 0) nk_input_key(ctx, NK_KEY_UP, k_state[NK_KEY_UP]);
+    if (k_state[NK_KEY_DOWN] >= 0) nk_input_key(ctx, NK_KEY_DOWN, k_state[NK_KEY_DOWN]);
+    if (k_state[NK_KEY_SCROLL_UP] >= 0) nk_input_key(ctx, NK_KEY_SCROLL_UP, k_state[NK_KEY_SCROLL_UP]);
+    if (k_state[NK_KEY_SCROLL_DOWN] >= 0) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, k_state[NK_KEY_SCROLL_DOWN]);
 
-    nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS||
                                     glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
 
     if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
         glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
-        nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, glfwGetKey(win, GLFW_KEY_A) == GLFW_PRESS);
+        /* Note these are physical keys and won't respect any layouts/key mapping */
+        if (k_state[NK_KEY_COPY] >= 0) nk_input_key(ctx, NK_KEY_COPY, k_state[NK_KEY_COPY]);
+        if (k_state[NK_KEY_PASTE] >= 0) nk_input_key(ctx, NK_KEY_PASTE, k_state[NK_KEY_PASTE]);
+        if (k_state[NK_KEY_CUT] >= 0) nk_input_key(ctx, NK_KEY_CUT, k_state[NK_KEY_CUT]);
+        if (k_state[NK_KEY_TEXT_UNDO] >= 0) nk_input_key(ctx, NK_KEY_TEXT_UNDO, k_state[NK_KEY_TEXT_UNDO]);
+        if (k_state[NK_KEY_TEXT_REDO] >= 0) nk_input_key(ctx, NK_KEY_TEXT_REDO, k_state[NK_KEY_TEXT_REDO]);
+        if (k_state[NK_KEY_TEXT_LINE_START] >= 0) nk_input_key(ctx, NK_KEY_TEXT_LINE_START, k_state[NK_KEY_TEXT_LINE_START]);
+        if (k_state[NK_KEY_TEXT_LINE_END] >= 0) nk_input_key(ctx, NK_KEY_TEXT_LINE_END, k_state[NK_KEY_TEXT_LINE_END]);
+        if (k_state[NK_KEY_TEXT_SELECT_ALL] >= 0) nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, k_state[NK_KEY_TEXT_SELECT_ALL]);
+        if (k_state[NK_KEY_LEFT] >= 0) nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, k_state[NK_KEY_LEFT]);
+        if (k_state[NK_KEY_RIGHT] >= 0) nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, k_state[NK_KEY_RIGHT]);
     } else {
-        nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
+        if (k_state[NK_KEY_LEFT] >= 0) nk_input_key(ctx, NK_KEY_LEFT, k_state[NK_KEY_LEFT]);
+        if (k_state[NK_KEY_RIGHT] >= 0) nk_input_key(ctx, NK_KEY_RIGHT, k_state[NK_KEY_RIGHT]);
         nk_input_key(ctx, NK_KEY_COPY, 0);
         nk_input_key(ctx, NK_KEY_PASTE, 0);
         nk_input_key(ctx, NK_KEY_CUT, 0);
@@ -377,6 +433,10 @@ nk_glfw3_new_frame(void)
     nk_input_button(ctx, NK_BUTTON_DOUBLE, (int)glfw.double_click_pos.x, (int)glfw.double_click_pos.y, glfw.is_double_click_down);
     nk_input_scroll(ctx, glfw.scroll);
     nk_input_end(&glfw.ctx);
+
+    /* clear after nk_input_end (-1 since we're doing up/down boolean) */
+    memset(glfw.key_events, -1, sizeof(glfw.key_events));
+
     glfw.text_len = 0;
     glfw.scroll = nk_vec2(0,0);
 }

+ 0 - 1
demo/glfw_opengl3/main.c

@@ -21,7 +21,6 @@
 #define NK_INCLUDE_DEFAULT_FONT
 #define NK_IMPLEMENTATION
 #define NK_GLFW_GL3_IMPLEMENTATION
-#define NK_KEYSTATE_BASED_INPUT
 #include "../../nuklear.h"
 #include "nuklear_glfw_gl3.h"
 

+ 73 - 21
demo/glfw_opengl3/nuklear_glfw_gl3.h

@@ -48,6 +48,7 @@ struct nk_glfw {
     struct nk_font_atlas atlas;
     struct nk_vec2 fb_scale;
     unsigned int text[NK_GLFW_TEXT_MAX];
+    nk_char key_events[NK_KEY_MAX];
     int text_len;
     struct nk_vec2 scroll;
     double last_button_click;
@@ -67,6 +68,7 @@ NK_API void                 nk_glfw3_device_destroy(struct nk_glfw* glfw);
 NK_API void                 nk_glfw3_device_create(struct nk_glfw* glfw);
 
 NK_API void                 nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
+NK_API void                 nk_glfw3_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods);
 NK_API void                 nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff);
 NK_API void                 nk_glfw3_mouse_button_callback(GLFWwindow *win, int button, int action, int mods);
 
@@ -320,6 +322,49 @@ nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint)
         glfw->text[glfw->text_len++] = codepoint;
 }
 
+NK_API void
+nk_glfw3_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods)
+{
+    struct nk_glfw* glfw = (struct nk_glfw *)glfwGetWindowUserPointer(win);
+    /*
+     * convert GLFW_REPEAT to down (technically GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT are
+     * already 0, 1, 2 but just to be clearer)
+     */
+    nk_char a = (action == GLFW_RELEASE) ? nk_false : nk_true;
+
+    NK_UNUSED(scancode);
+    NK_UNUSED(mods);
+
+    switch (key) {
+    case GLFW_KEY_DELETE:    glfw->key_events[NK_KEY_DEL] = a; break;
+    case GLFW_KEY_TAB:       glfw->key_events[NK_KEY_TAB] = a; break;
+    case GLFW_KEY_BACKSPACE: glfw->key_events[NK_KEY_BACKSPACE] = a; break;
+    case GLFW_KEY_UP:        glfw->key_events[NK_KEY_UP] = a; break;
+    case GLFW_KEY_DOWN:      glfw->key_events[NK_KEY_DOWN] = a; break;
+    case GLFW_KEY_LEFT:      glfw->key_events[NK_KEY_LEFT] = a; break;
+    case GLFW_KEY_RIGHT:     glfw->key_events[NK_KEY_RIGHT] = a; break;
+
+    case GLFW_KEY_PAGE_UP:   glfw->key_events[NK_KEY_SCROLL_UP] = a; break;
+    case GLFW_KEY_PAGE_DOWN: glfw->key_events[NK_KEY_SCROLL_DOWN] = a; break;
+
+    case GLFW_KEY_C:         glfw->key_events[NK_KEY_COPY] = a; break;
+    case GLFW_KEY_V:         glfw->key_events[NK_KEY_PASTE] = a; break;
+    case GLFW_KEY_X:         glfw->key_events[NK_KEY_CUT] = a; break;
+    case GLFW_KEY_Z:         glfw->key_events[NK_KEY_TEXT_UNDO] = a; break;
+    case GLFW_KEY_R:         glfw->key_events[NK_KEY_TEXT_REDO] = a; break;
+    case GLFW_KEY_B:         glfw->key_events[NK_KEY_TEXT_LINE_START] = a; break;
+    case GLFW_KEY_E:         glfw->key_events[NK_KEY_TEXT_LINE_END] = a; break;
+    case GLFW_KEY_A:         glfw->key_events[NK_KEY_TEXT_SELECT_ALL] = a; break;
+
+    case GLFW_KEY_ENTER:
+    case GLFW_KEY_KP_ENTER:
+        glfw->key_events[NK_KEY_ENTER] = a;
+        break;
+    default:
+        ;
+    }
+}
+
 NK_API void
 nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff)
 {
@@ -378,6 +423,7 @@ nk_glfw3_init(struct nk_glfw* glfw, GLFWwindow *win, enum nk_glfw_init_state ini
     if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
         glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
         glfwSetCharCallback(win, nk_glfw3_char_callback);
+        glfwSetKeyCallback(win, nk_glfw3_key_callback);
         glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
     }
     nk_init_default(&glfw->ctx, 0);
@@ -421,6 +467,7 @@ nk_glfw3_new_frame(struct nk_glfw* glfw)
     double x, y;
     struct nk_context *ctx = &glfw->ctx;
     struct GLFWwindow *win = glfw->win;
+    nk_char* k_state = glfw->key_events;
 
     /* update the timer */
     float delta_time_now = (float)glfwGetTime();
@@ -444,38 +491,39 @@ nk_glfw3_new_frame(struct nk_glfw* glfw)
         glfwSetInputMode(glfw->win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
 #endif
 
-    nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS ||
-                                    glfwGetKey(win, GLFW_KEY_KP_ENTER) == GLFW_PRESS);
+    if (k_state[NK_KEY_DEL] >= 0) nk_input_key(ctx, NK_KEY_DEL, k_state[NK_KEY_DEL]);
+    if (k_state[NK_KEY_ENTER] >= 0) nk_input_key(ctx, NK_KEY_ENTER, k_state[NK_KEY_ENTER]);
+
+    if (k_state[NK_KEY_TAB] >= 0) nk_input_key(ctx, NK_KEY_TAB, k_state[NK_KEY_TAB]);
+    if (k_state[NK_KEY_BACKSPACE] >= 0) nk_input_key(ctx, NK_KEY_BACKSPACE, k_state[NK_KEY_BACKSPACE]);
+    if (k_state[NK_KEY_UP] >= 0) nk_input_key(ctx, NK_KEY_UP, k_state[NK_KEY_UP]);
+    if (k_state[NK_KEY_DOWN] >= 0) nk_input_key(ctx, NK_KEY_DOWN, k_state[NK_KEY_DOWN]);
+    if (k_state[NK_KEY_SCROLL_UP] >= 0) nk_input_key(ctx, NK_KEY_SCROLL_UP, k_state[NK_KEY_SCROLL_UP]);
+    if (k_state[NK_KEY_SCROLL_DOWN] >= 0) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, k_state[NK_KEY_SCROLL_DOWN]);
 
-    nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS||
                                     glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
 
     if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
         glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
-        nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, glfwGetKey(win, GLFW_KEY_A) == GLFW_PRESS);
+        /* Note these are physical keys and won't respect any layouts/key mapping */
+        if (k_state[NK_KEY_COPY] >= 0) nk_input_key(ctx, NK_KEY_COPY, k_state[NK_KEY_COPY]);
+        if (k_state[NK_KEY_PASTE] >= 0) nk_input_key(ctx, NK_KEY_PASTE, k_state[NK_KEY_PASTE]);
+        if (k_state[NK_KEY_CUT] >= 0) nk_input_key(ctx, NK_KEY_CUT, k_state[NK_KEY_CUT]);
+        if (k_state[NK_KEY_TEXT_UNDO] >= 0) nk_input_key(ctx, NK_KEY_TEXT_UNDO, k_state[NK_KEY_TEXT_UNDO]);
+        if (k_state[NK_KEY_TEXT_REDO] >= 0) nk_input_key(ctx, NK_KEY_TEXT_REDO, k_state[NK_KEY_TEXT_REDO]);
+        if (k_state[NK_KEY_TEXT_LINE_START] >= 0) nk_input_key(ctx, NK_KEY_TEXT_LINE_START, k_state[NK_KEY_TEXT_LINE_START]);
+        if (k_state[NK_KEY_TEXT_LINE_END] >= 0) nk_input_key(ctx, NK_KEY_TEXT_LINE_END, k_state[NK_KEY_TEXT_LINE_END]);
+        if (k_state[NK_KEY_TEXT_SELECT_ALL] >= 0) nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, k_state[NK_KEY_TEXT_SELECT_ALL]);
+        if (k_state[NK_KEY_LEFT] >= 0) nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, k_state[NK_KEY_LEFT]);
+        if (k_state[NK_KEY_RIGHT] >= 0) nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, k_state[NK_KEY_RIGHT]);
     } else {
-        nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
+        if (k_state[NK_KEY_LEFT] >= 0) nk_input_key(ctx, NK_KEY_LEFT, k_state[NK_KEY_LEFT]);
+        if (k_state[NK_KEY_RIGHT] >= 0) nk_input_key(ctx, NK_KEY_RIGHT, k_state[NK_KEY_RIGHT]);
         nk_input_key(ctx, NK_KEY_COPY, 0);
         nk_input_key(ctx, NK_KEY_PASTE, 0);
         nk_input_key(ctx, NK_KEY_CUT, 0);
@@ -496,6 +544,10 @@ nk_glfw3_new_frame(struct nk_glfw* glfw)
     nk_input_button(ctx, NK_BUTTON_DOUBLE, (int)glfw->double_click_pos.x, (int)glfw->double_click_pos.y, glfw->is_double_click_down);
     nk_input_scroll(ctx, glfw->scroll);
     nk_input_end(&glfw->ctx);
+
+    /* clear after nk_input_end (-1 since we're doing up/down boolean) */
+    memset(glfw->key_events, -1, sizeof(glfw->key_events));
+
     glfw->text_len = 0;
     glfw->scroll = nk_vec2(0,0);
 }

+ 0 - 1
demo/glfw_opengl4/main.c

@@ -21,7 +21,6 @@
 #define NK_INCLUDE_DEFAULT_FONT
 #define NK_IMPLEMENTATION
 #define NK_GLFW_GL4_IMPLEMENTATION
-#define NK_KEYSTATE_BASED_INPUT
 #include "../../nuklear.h"
 #include "nuklear_glfw_gl4.h"
 

+ 88 - 21
demo/glfw_opengl4/nuklear_glfw_gl4.h

@@ -32,6 +32,7 @@ NK_API void                 nk_glfw3_device_destroy(void);
 NK_API void                 nk_glfw3_device_create(void);
 
 NK_API void                 nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
+NK_API void                 nk_glfw3_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods);
 NK_API void                 nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff);
 NK_API void                 nk_glfw3_mouse_button_callback(GLFWwindow *win, int button, int action, int mods);
 
@@ -97,11 +98,13 @@ static struct nk_glfw {
     struct nk_font_atlas atlas;
     struct nk_vec2 fb_scale;
     unsigned int text[NK_GLFW_TEXT_MAX];
+    nk_char key_events[NK_KEY_MAX];
     int text_len;
     struct nk_vec2 scroll;
     double last_button_click;
     int is_double_click_down;
     struct nk_vec2 double_click_pos;
+    float delta_time_seconds_last;
 } glfw;
 
 #define NK_SHADER_VERSION "#version 450 core\n"
@@ -409,6 +412,56 @@ nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint)
         glfw.text[glfw.text_len++] = codepoint;
 }
 
+NK_API void
+nk_glfw3_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods)
+{
+    /*
+     * convert GLFW_REPEAT to down (technically GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT are
+     * already 0, 1, 2 but just to be clearer)
+     */
+    nk_char a = (action == GLFW_RELEASE) ? nk_false : nk_true;
+
+    NK_UNUSED(win);
+    NK_UNUSED(scancode);
+    NK_UNUSED(mods);
+
+    switch (key) {
+    case GLFW_KEY_DELETE:    glfw.key_events[NK_KEY_DEL] = a; break;
+    case GLFW_KEY_TAB:       glfw.key_events[NK_KEY_TAB] = a; break;
+    case GLFW_KEY_BACKSPACE: glfw.key_events[NK_KEY_BACKSPACE] = a; break;
+    case GLFW_KEY_UP:        glfw.key_events[NK_KEY_UP] = a; break;
+    case GLFW_KEY_DOWN:      glfw.key_events[NK_KEY_DOWN] = a; break;
+    case GLFW_KEY_LEFT:      glfw.key_events[NK_KEY_LEFT] = a; break;
+    case GLFW_KEY_RIGHT:     glfw.key_events[NK_KEY_RIGHT] = a; break;
+
+    case GLFW_KEY_PAGE_UP:   glfw.key_events[NK_KEY_SCROLL_UP] = a; break;
+    case GLFW_KEY_PAGE_DOWN: glfw.key_events[NK_KEY_SCROLL_DOWN] = a; break;
+
+    /* have to add all keys used for nuklear to get correct repeat behavior
+     * NOTE these are scancodes so your custom layout won't matter unfortunately
+     * Also while including everything will prevent unnecessary input calls,
+     * only the ones with visible effects really matter, ie paste, undo, redo
+     * selecting all, copying or cutting 40 times before you release the keys
+     * doesn't actually cause any visible problems */
+
+    case GLFW_KEY_C:         glfw.key_events[NK_KEY_COPY] = a; break;
+    case GLFW_KEY_V:         glfw.key_events[NK_KEY_PASTE] = a; break;
+    case GLFW_KEY_X:         glfw.key_events[NK_KEY_CUT] = a; break;
+    case GLFW_KEY_Z:         glfw.key_events[NK_KEY_TEXT_UNDO] = a; break;
+    case GLFW_KEY_R:         glfw.key_events[NK_KEY_TEXT_REDO] = a; break;
+    case GLFW_KEY_B:         glfw.key_events[NK_KEY_TEXT_LINE_START] = a; break;
+    case GLFW_KEY_E:         glfw.key_events[NK_KEY_TEXT_LINE_END] = a; break;
+    case GLFW_KEY_A:         glfw.key_events[NK_KEY_TEXT_SELECT_ALL] = a; break;
+
+    case GLFW_KEY_ENTER:
+    case GLFW_KEY_KP_ENTER:
+        glfw.key_events[NK_KEY_ENTER] = a;
+        break;
+    default:
+        ;
+    }
+}
+
 NK_API void
 nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff)
 {
@@ -464,6 +517,7 @@ nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state,
     if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
         glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
         glfwSetCharCallback(win, nk_glfw3_char_callback);
+        glfwSetKeyCallback(win, nk_glfw3_key_callback);
         glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
     }
     nk_init_default(&glfw.ctx, 0);
@@ -480,6 +534,8 @@ nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state,
     glfw.is_double_click_down = nk_false;
     glfw.double_click_pos = nk_vec2(0, 0);
 
+    glfw.delta_time_seconds_last = (float)glfwGetTime();
+
     return &glfw.ctx;
 }
 
@@ -509,6 +565,12 @@ nk_glfw3_new_frame(void)
     double x, y;
     struct nk_context *ctx = &glfw.ctx;
     struct GLFWwindow *win = glfw.win;
+    nk_char* k_state = glfw.key_events;
+
+    /* update the timer */
+    float delta_time_now = (float)glfwGetTime();
+    glfw.ctx.delta_time_seconds = delta_time_now - glfw.delta_time_seconds_last;
+    glfw.delta_time_seconds_last = delta_time_now;
 
     glfwGetWindowSize(win, &glfw.width, &glfw.height);
     glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height);
@@ -527,38 +589,39 @@ nk_glfw3_new_frame(void)
         glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
 #endif
 
-    nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS ||
-                                    glfwGetKey(win, GLFW_KEY_KP_ENTER) == GLFW_PRESS);
+    if (k_state[NK_KEY_DEL] >= 0) nk_input_key(ctx, NK_KEY_DEL, k_state[NK_KEY_DEL]);
+    if (k_state[NK_KEY_ENTER] >= 0) nk_input_key(ctx, NK_KEY_ENTER, k_state[NK_KEY_ENTER]);
+
+    if (k_state[NK_KEY_TAB] >= 0) nk_input_key(ctx, NK_KEY_TAB, k_state[NK_KEY_TAB]);
+    if (k_state[NK_KEY_BACKSPACE] >= 0) nk_input_key(ctx, NK_KEY_BACKSPACE, k_state[NK_KEY_BACKSPACE]);
+    if (k_state[NK_KEY_UP] >= 0) nk_input_key(ctx, NK_KEY_UP, k_state[NK_KEY_UP]);
+    if (k_state[NK_KEY_DOWN] >= 0) nk_input_key(ctx, NK_KEY_DOWN, k_state[NK_KEY_DOWN]);
+    if (k_state[NK_KEY_SCROLL_UP] >= 0) nk_input_key(ctx, NK_KEY_SCROLL_UP, k_state[NK_KEY_SCROLL_UP]);
+    if (k_state[NK_KEY_SCROLL_DOWN] >= 0) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, k_state[NK_KEY_SCROLL_DOWN]);
 
-    nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS);
     nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS||
                                     glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
 
     if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
         glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
-        nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, glfwGetKey(win, GLFW_KEY_A) == GLFW_PRESS);
+        /* Note these are physical keys and won't respect any layouts/key mapping */
+        if (k_state[NK_KEY_COPY] >= 0) nk_input_key(ctx, NK_KEY_COPY, k_state[NK_KEY_COPY]);
+        if (k_state[NK_KEY_PASTE] >= 0) nk_input_key(ctx, NK_KEY_PASTE, k_state[NK_KEY_PASTE]);
+        if (k_state[NK_KEY_CUT] >= 0) nk_input_key(ctx, NK_KEY_CUT, k_state[NK_KEY_CUT]);
+        if (k_state[NK_KEY_TEXT_UNDO] >= 0) nk_input_key(ctx, NK_KEY_TEXT_UNDO, k_state[NK_KEY_TEXT_UNDO]);
+        if (k_state[NK_KEY_TEXT_REDO] >= 0) nk_input_key(ctx, NK_KEY_TEXT_REDO, k_state[NK_KEY_TEXT_REDO]);
+        if (k_state[NK_KEY_TEXT_LINE_START] >= 0) nk_input_key(ctx, NK_KEY_TEXT_LINE_START, k_state[NK_KEY_TEXT_LINE_START]);
+        if (k_state[NK_KEY_TEXT_LINE_END] >= 0) nk_input_key(ctx, NK_KEY_TEXT_LINE_END, k_state[NK_KEY_TEXT_LINE_END]);
+        if (k_state[NK_KEY_TEXT_SELECT_ALL] >= 0) nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, k_state[NK_KEY_TEXT_SELECT_ALL]);
+        if (k_state[NK_KEY_LEFT] >= 0) nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, k_state[NK_KEY_LEFT]);
+        if (k_state[NK_KEY_RIGHT] >= 0) nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, k_state[NK_KEY_RIGHT]);
     } else {
-        nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
+        if (k_state[NK_KEY_LEFT] >= 0) nk_input_key(ctx, NK_KEY_LEFT, k_state[NK_KEY_LEFT]);
+        if (k_state[NK_KEY_RIGHT] >= 0) nk_input_key(ctx, NK_KEY_RIGHT, k_state[NK_KEY_RIGHT]);
         nk_input_key(ctx, NK_KEY_COPY, 0);
         nk_input_key(ctx, NK_KEY_PASTE, 0);
         nk_input_key(ctx, NK_KEY_CUT, 0);
@@ -579,6 +642,10 @@ nk_glfw3_new_frame(void)
     nk_input_button(ctx, NK_BUTTON_DOUBLE, (int)glfw.double_click_pos.x, (int)glfw.double_click_pos.y, glfw.is_double_click_down);
     nk_input_scroll(ctx, glfw.scroll);
     nk_input_end(&glfw.ctx);
+
+    /* clear after nk_input_end (-1 since we're doing up/down boolean) */
+    memset(glfw.key_events, -1, sizeof(glfw.key_events));
+
     glfw.text_len = 0;
     glfw.scroll = nk_vec2(0,0);
 }

+ 0 - 1
demo/glfw_vulkan/main.c

@@ -18,7 +18,6 @@
 #define NK_INCLUDE_DEFAULT_FONT
 #define NK_IMPLEMENTATION
 #define NK_GLFW_VULKAN_IMPLEMENTATION
-#define NK_KEYSTATE_BASED_INPUT
 #include "../../nuklear.h"
 #include "nuklear_glfw_vulkan.h"
 

+ 97 - 55
demo/glfw_vulkan/nuklear_glfw_vulkan.h

@@ -281,6 +281,7 @@ NK_API void nk_glfw3_device_create(
     uint32_t framebuffer_width, uint32_t framebuffer_height);
 
 NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
+NK_API void nk_glfw3_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods);
 NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff);
 NK_API void nk_glfw3_mouse_button_callback(GLFWwindow *win, int button,
                                            int action, int mods);
@@ -372,7 +373,9 @@ static struct nk_glfw {
     struct nk_glfw_device vulkan;
     struct nk_context ctx;
     struct nk_font_atlas atlas;
+    struct nk_vec2 fb_scale;
     unsigned int text[NK_GLFW_TEXT_MAX];
+    nk_char key_events[NK_KEY_MAX];
     int text_len;
     struct nk_vec2 scroll;
     double last_button_click;
@@ -1251,12 +1254,18 @@ NK_API void nk_glfw3_new_frame(void) {
     double x, y;
     struct nk_context *ctx = &glfw.ctx;
     struct GLFWwindow *win = glfw.win;
+    nk_char* k_state = glfw.key_events;
 
     /* update the timer */
     float delta_time_now = (float)glfwGetTime();
     glfw.ctx.delta_time_seconds = delta_time_now - glfw.delta_time_seconds_last;
     glfw.delta_time_seconds_last = delta_time_now;
 
+    glfwGetWindowSize(win, &glfw.width, &glfw.height);
+    glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height);
+    glfw.fb_scale.x = (float)glfw.display_width/(float)glfw.width;
+    glfw.fb_scale.y = (float)glfw.display_height/(float)glfw.height;
+
     nk_input_begin(ctx);
     for (i = 0; i < glfw.text_len; ++i)
         nk_input_unicode(ctx, glfw.text[i]);
@@ -1269,61 +1278,39 @@ NK_API void nk_glfw3_new_frame(void) {
         glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
 #endif
 
-    nk_input_key(ctx, NK_KEY_DEL,
-                 glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_ENTER,
-                 glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS ||
-                 glfwGetKey(win, GLFW_KEY_KP_ENTER) == GLFW_PRESS);
-
-    nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_BACKSPACE,
-                 glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_DOWN,
-                 glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_TEXT_START,
-                 glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_TEXT_END,
-                 glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_START,
-                 glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_END,
-                 glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_DOWN,
-                 glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SCROLL_UP,
-                 glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS);
-    nk_input_key(ctx, NK_KEY_SHIFT,
-                 glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS ||
-                     glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
+    if (k_state[NK_KEY_DEL] >= 0) nk_input_key(ctx, NK_KEY_DEL, k_state[NK_KEY_DEL]);
+    if (k_state[NK_KEY_ENTER] >= 0) nk_input_key(ctx, NK_KEY_ENTER, k_state[NK_KEY_ENTER]);
+
+    if (k_state[NK_KEY_TAB] >= 0) nk_input_key(ctx, NK_KEY_TAB, k_state[NK_KEY_TAB]);
+    if (k_state[NK_KEY_BACKSPACE] >= 0) nk_input_key(ctx, NK_KEY_BACKSPACE, k_state[NK_KEY_BACKSPACE]);
+    if (k_state[NK_KEY_UP] >= 0) nk_input_key(ctx, NK_KEY_UP, k_state[NK_KEY_UP]);
+    if (k_state[NK_KEY_DOWN] >= 0) nk_input_key(ctx, NK_KEY_DOWN, k_state[NK_KEY_DOWN]);
+    if (k_state[NK_KEY_SCROLL_UP] >= 0) nk_input_key(ctx, NK_KEY_SCROLL_UP, k_state[NK_KEY_SCROLL_UP]);
+    if (k_state[NK_KEY_SCROLL_DOWN] >= 0) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, k_state[NK_KEY_SCROLL_DOWN]);
+
+    nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
+    nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
+    nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS);
+    nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS);
+    nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS||
+                                    glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
 
     if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
         glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
-        nk_input_key(ctx, NK_KEY_COPY,
-                     glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_PASTE,
-                     glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_CUT,
-                     glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_UNDO,
-                     glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_REDO,
-                     glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT,
-                     glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT,
-                     glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_LINE_START,
-                     glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_LINE_END,
-                     glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL,
-                     glfwGetKey(win, GLFW_KEY_A) == GLFW_PRESS);
+        /* Note these are physical keys and won't respect any layouts/key mapping */
+        if (k_state[NK_KEY_COPY] >= 0) nk_input_key(ctx, NK_KEY_COPY, k_state[NK_KEY_COPY]);
+        if (k_state[NK_KEY_PASTE] >= 0) nk_input_key(ctx, NK_KEY_PASTE, k_state[NK_KEY_PASTE]);
+        if (k_state[NK_KEY_CUT] >= 0) nk_input_key(ctx, NK_KEY_CUT, k_state[NK_KEY_CUT]);
+        if (k_state[NK_KEY_TEXT_UNDO] >= 0) nk_input_key(ctx, NK_KEY_TEXT_UNDO, k_state[NK_KEY_TEXT_UNDO]);
+        if (k_state[NK_KEY_TEXT_REDO] >= 0) nk_input_key(ctx, NK_KEY_TEXT_REDO, k_state[NK_KEY_TEXT_REDO]);
+        if (k_state[NK_KEY_TEXT_LINE_START] >= 0) nk_input_key(ctx, NK_KEY_TEXT_LINE_START, k_state[NK_KEY_TEXT_LINE_START]);
+        if (k_state[NK_KEY_TEXT_LINE_END] >= 0) nk_input_key(ctx, NK_KEY_TEXT_LINE_END, k_state[NK_KEY_TEXT_LINE_END]);
+        if (k_state[NK_KEY_TEXT_SELECT_ALL] >= 0) nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, k_state[NK_KEY_TEXT_SELECT_ALL]);
+        if (k_state[NK_KEY_LEFT] >= 0) nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, k_state[NK_KEY_LEFT]);
+        if (k_state[NK_KEY_RIGHT] >= 0) nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, k_state[NK_KEY_RIGHT]);
     } else {
-        nk_input_key(ctx, NK_KEY_LEFT,
-                     glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
-        nk_input_key(ctx, NK_KEY_RIGHT,
-                     glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
+        if (k_state[NK_KEY_LEFT] >= 0) nk_input_key(ctx, NK_KEY_LEFT, k_state[NK_KEY_LEFT]);
+        if (k_state[NK_KEY_RIGHT] >= 0) nk_input_key(ctx, NK_KEY_RIGHT, k_state[NK_KEY_RIGHT]);
         nk_input_key(ctx, NK_KEY_COPY, 0);
         nk_input_key(ctx, NK_KEY_PASTE, 0);
         nk_input_key(ctx, NK_KEY_CUT, 0);
@@ -1352,6 +1339,10 @@ NK_API void nk_glfw3_new_frame(void) {
                     (int)glfw.double_click_pos.y, glfw.is_double_click_down);
     nk_input_scroll(ctx, glfw.scroll);
     nk_input_end(&glfw.ctx);
+
+    /* clear after nk_input_end (-1 since we're doing up/down boolean) */
+    memset(glfw.key_events, -1, sizeof(glfw.key_events));
+
     glfw.text_len = 0;
     glfw.scroll = nk_vec2(0, 0);
 }
@@ -1520,10 +1511,10 @@ VkSemaphore nk_glfw3_render(VkQueue graphics_queue, uint32_t buffer_index,
             if (!cmd->elem_count)
                 continue;
 
-            scissor.offset.x = (int32_t)(NK_MAX(cmd->clip_rect.x, 0.f));
-            scissor.offset.y = (int32_t)(NK_MAX(cmd->clip_rect.y, 0.f));
-            scissor.extent.width = (uint32_t)(cmd->clip_rect.w);
-            scissor.extent.height = (uint32_t)(cmd->clip_rect.h);
+            scissor.offset.x = (int32_t)(NK_MAX(cmd->clip_rect.x, 0.f) * glfw.fb_scale.x);
+            scissor.offset.y = (int32_t)(NK_MAX(cmd->clip_rect.y, 0.f) * glfw.fb_scale.y);
+            scissor.extent.width = (uint32_t)(cmd->clip_rect.w * glfw.fb_scale.x);
+            scissor.extent.height = (uint32_t)(cmd->clip_rect.h * glfw.fb_scale.y);
             vkCmdSetScissor(command_buffer, 0, 1, &scissor);
             vkCmdDrawIndexed(command_buffer, cmd->elem_count, 1, index_offset,
                              0, 0);
@@ -1566,6 +1557,56 @@ NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) {
         glfw.text[glfw.text_len++] = codepoint;
 }
 
+NK_API void
+nk_glfw3_key_callback(GLFWwindow *win, int key, int scancode, int action, int mods)
+{
+    /*
+     * convert GLFW_REPEAT to down (technically GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT are
+     * already 0, 1, 2 but just to be clearer)
+     */
+    nk_char a = (action == GLFW_RELEASE) ? nk_false : nk_true;
+
+    NK_UNUSED(win);
+    NK_UNUSED(scancode);
+    NK_UNUSED(mods);
+
+    switch (key) {
+    case GLFW_KEY_DELETE:    glfw.key_events[NK_KEY_DEL] = a; break;
+    case GLFW_KEY_TAB:       glfw.key_events[NK_KEY_TAB] = a; break;
+    case GLFW_KEY_BACKSPACE: glfw.key_events[NK_KEY_BACKSPACE] = a; break;
+    case GLFW_KEY_UP:        glfw.key_events[NK_KEY_UP] = a; break;
+    case GLFW_KEY_DOWN:      glfw.key_events[NK_KEY_DOWN] = a; break;
+    case GLFW_KEY_LEFT:      glfw.key_events[NK_KEY_LEFT] = a; break;
+    case GLFW_KEY_RIGHT:     glfw.key_events[NK_KEY_RIGHT] = a; break;
+
+    case GLFW_KEY_PAGE_UP:   glfw.key_events[NK_KEY_SCROLL_UP] = a; break;
+    case GLFW_KEY_PAGE_DOWN: glfw.key_events[NK_KEY_SCROLL_DOWN] = a; break;
+
+    /* have to add all keys used for nuklear to get correct repeat behavior
+     * NOTE these are scancodes so your custom layout won't matter unfortunately
+     * Also while including everything will prevent unnecessary input calls,
+     * only the ones with visible effects really matter, ie paste, undo, redo
+     * selecting all, copying or cutting 40 times before you release the keys
+     * doesn't actually cause any visible problems */
+
+    case GLFW_KEY_C:         glfw.key_events[NK_KEY_COPY] = a; break;
+    case GLFW_KEY_V:         glfw.key_events[NK_KEY_PASTE] = a; break;
+    case GLFW_KEY_X:         glfw.key_events[NK_KEY_CUT] = a; break;
+    case GLFW_KEY_Z:         glfw.key_events[NK_KEY_TEXT_UNDO] = a; break;
+    case GLFW_KEY_R:         glfw.key_events[NK_KEY_TEXT_REDO] = a; break;
+    case GLFW_KEY_B:         glfw.key_events[NK_KEY_TEXT_LINE_START] = a; break;
+    case GLFW_KEY_E:         glfw.key_events[NK_KEY_TEXT_LINE_END] = a; break;
+    case GLFW_KEY_A:         glfw.key_events[NK_KEY_TEXT_SELECT_ALL] = a; break;
+
+    case GLFW_KEY_ENTER:
+    case GLFW_KEY_KP_ENTER:
+        glfw.key_events[NK_KEY_ENTER] = a;
+        break;
+    default:
+        ;
+    }
+}
+
 NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff,
                                      double yoff) {
     (void)win;
@@ -1627,6 +1668,7 @@ nk_glfw3_init(GLFWwindow *win, VkDevice logical_device,
     if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
         glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
         glfwSetCharCallback(win, nk_glfw3_char_callback);
+        glfwSetKeyCallback(win, nk_glfw3_key_callback);
         glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
     }
     nk_init_default(&glfw.ctx, 0);