Просмотр исходного кода

Merge branch 'master' of github.com:Immediate-Mode-UI/Nuklear into patch-1

Rob Loach 1 год назад
Родитель
Сommit
b4e9e924cf
74 измененных файлов с 7118 добавлено и 671 удалено
  1. 33 5
      .github/workflows/ccpp.yml
  2. 4 3
      .github/workflows/create-tag.yml
  3. 1 1
      clib.json
  4. 1 0
      demo/allegro5/nuklear_allegro5.h
  5. 446 267
      demo/common/filebrowser/stb_image.h
  6. 64 46
      demo/common/overview.c
  7. 1 0
      demo/d3d11/nuklear_d3d11.h
  8. 1 0
      demo/d3d12/nuklear_d3d12.h
  9. 1 0
      demo/d3d9/nuklear_d3d9.h
  10. 1 1
      demo/gdi/nuklear_gdi.h
  11. 1 1
      demo/gdi_native_nuklear/nuklear_gdi.h
  12. 2 0
      demo/glfw_opengl2/nuklear_glfw_gl2.h
  13. 3 0
      demo/glfw_opengl3/nuklear_glfw_gl3.h
  14. 3 0
      demo/glfw_opengl4/nuklear_glfw_gl4.h
  15. 4 0
      demo/glfw_vulkan/.clang-format
  16. 3 0
      demo/glfw_vulkan/.gitignore
  17. 29 0
      demo/glfw_vulkan/Makefile
  18. 52 0
      demo/glfw_vulkan/README.md
  19. 2229 0
      demo/glfw_vulkan/main.c
  20. 1649 0
      demo/glfw_vulkan/nuklear_glfw_vulkan.h
  21. 12 0
      demo/glfw_vulkan/shaders/demo.frag
  22. 10 0
      demo/glfw_vulkan/shaders/demo.vert
  23. 11 0
      demo/glfw_vulkan/src/Makefile
  24. 5 0
      demo/glfw_vulkan/src/README.md
  25. 1426 0
      demo/glfw_vulkan/src/nuklear_glfw_vulkan.in.h
  26. 13 0
      demo/glfw_vulkan/src/nuklearshaders/nuklear.frag
  27. 23 0
      demo/glfw_vulkan/src/nuklearshaders/nuklear.vert
  28. 4 0
      demo/sdl2surface_rawfb/sdl2surface_rawfb.h
  29. 1 0
      demo/sdl_opengl2/main.c
  30. 15 8
      demo/sdl_opengl2/nuklear_sdl_gl2.h
  31. 3 1
      demo/sdl_opengl3/main.c
  32. 15 8
      demo/sdl_opengl3/nuklear_sdl_gl3.h
  33. 1 0
      demo/sdl_opengles2/main.c
  34. 15 9
      demo/sdl_opengles2/nuklear_sdl_gles2.h
  35. 1 0
      demo/sdl_renderer/main.c
  36. 15 10
      demo/sdl_renderer/nuklear_sdl_renderer.h
  37. 1 1
      demo/sfml_opengl2/Readme.md
  38. 2 0
      demo/sfml_opengl2/nuklear_sfml_gl2.h
  39. 1 1
      demo/sfml_opengl3/Readme.md
  40. 2 1
      demo/sfml_opengl3/nuklear_sfml_gl3.h
  41. 4 0
      demo/wayland_rawfb/nuklear_raw_wayland.h
  42. 3 0
      demo/x11/nuklear_xlib.h
  43. 1 0
      demo/x11_opengl3/nuklear_xlib_gl3.h
  44. 4 0
      demo/x11_rawfb/nuklear_rawfb.h
  45. 3 0
      demo/x11_rawfb/nuklear_xlib.h
  46. 3 0
      demo/x11_xft/nuklear_xlib.h
  47. 5 5
      doc/index.html
  48. 2 1
      example/file_browser.c
  49. 347 86
      nuklear.h
  50. 6 3
      src/CHANGELOG
  51. 62 5
      src/nuklear.h
  52. 23 16
      src/nuklear_button.c
  53. 24 14
      src/nuklear_chart.c
  54. 10 0
      src/nuklear_color.c
  55. 1 1
      src/nuklear_color_picker.c
  56. 42 33
      src/nuklear_combo.c
  57. 32 27
      src/nuklear_contextual.c
  58. 15 4
      src/nuklear_edit.c
  59. 7 7
      src/nuklear_internal.h
  60. 5 5
      src/nuklear_menu.c
  61. 2 2
      src/nuklear_panel.c
  62. 3 3
      src/nuklear_popup.c
  63. 9 9
      src/nuklear_progress.c
  64. 8 6
      src/nuklear_property.c
  65. 9 6
      src/nuklear_selectable.c
  66. 10 9
      src/nuklear_slider.c
  67. 77 17
      src/nuklear_style.c
  68. 2 2
      src/nuklear_text.c
  69. 154 35
      src/nuklear_toggle.c
  70. 1 1
      src/nuklear_tooltip.c
  71. 10 9
      src/nuklear_tree.c
  72. 127 0
      src/nuklear_widget.c
  73. 1 0
      src/nuklear_window.c
  74. 2 2
      src/stb_rect_pack.h

+ 33 - 5
.github/workflows/ccpp.yml

@@ -8,14 +8,42 @@ jobs:
     runs-on: ubuntu-latest
     
     steps:
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v4
     - name: apt-update
       run: sudo apt-get update -qq
-    - name: apt get glfw
-      run: sudo apt-get install -y --no-install-recommends libglfw3 libglfw3-dev libglew-dev
-    - name: build opengl2
+    - name: apt get demo-libs
+      run: sudo apt-get install -y --no-install-recommends liballegro5-dev liballegro-image5-dev liballegro-ttf5-dev libglfw3 libglfw3-dev libglew-dev libsdl2-dev libwayland-dev libx11-dev libxft-dev wayland-protocols
+    - name: build allegro5
+      run: make -C demo/allegro5
+    - name: build glfw_opengl2
       run: make -C demo/glfw_opengl2
-    - name: build opengl3
+    - name: build glfw_opengl3
       run: make -C demo/glfw_opengl3
+    - name: build glfw_opengl4
+      run: make -C demo/glfw_opengl4
+#    - name: build glfw_vulkan
+#      run: make -C demo/glfw_vulkan
+    - name: build sdl_opengl2
+      run: make -C demo/sdl_opengl2
+    - name: build sdl_opengl3
+      run: make -C demo/sdl_opengl3
+    - name: build sdl_opengles2
+      run: make -C demo/sdl_opengles2
+    - name: build sdl_renderer
+      run: make -C demo/sdl_renderer
+    - name: build sdl2surface_rawfb
+      run: make -C demo/sdl2surface_rawfb
+    - name: build wayland_rawfb
+      run: make -C demo/wayland_rawfb
+    - name: build x11
+      run: make -C demo/x11
+    - name: build x11_opengl2
+      run: make -C demo/x11_opengl2
+    - name: build x11_opengl3
+      run: make -C demo/x11_opengl3
+    - name: build x11_rawfb
+      run: make -C demo/x11_rawfb
+    - name: build x11_xft
+      run: make -C demo/x11_xft
     - name: build example
       run: make -C example

+ 4 - 3
.github/workflows/create-tag.yml

@@ -9,8 +9,9 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
-    - uses: butlerlogic/action-autotag@stable
-      with:
+    - uses: actions/checkout@v4
+    - uses: butlerlogic/action-autotag@1.1.2
+      env:
         GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+      with:
         root: clib.json

+ 1 - 1
clib.json

@@ -1,6 +1,6 @@
 {
   "name": "nuklear",
-  "version": "4.10.7",
+  "version": "4.12.2",
   "repo": "Immediate-Mode-UI/Nuklear",
   "description": "A small ANSI C gui toolkit",
   "keywords": ["gl", "ui", "toolkit"],

+ 1 - 0
demo/allegro5/nuklear_allegro5.h

@@ -46,6 +46,7 @@ NK_API void                   nk_allegro5_font_set_font(NkAllegro5Font *font);
  * ===============================================================
  */
 #ifdef NK_ALLEGRO5_IMPLEMENTATION
+#include <stdio.h>
 
 #ifndef NK_ALLEGRO5_TEXT_MAX
 #define NK_ALLEGRO5_TEXT_MAX 256

Разница между файлами не показана из-за своего большого размера
+ 446 - 267
demo/common/filebrowser/stb_image.h


+ 64 - 46
demo/common/overview.c

@@ -2,16 +2,18 @@ static int
 overview(struct nk_context *ctx)
 {
     /* window flags */
-    static int show_menu = nk_true;
+    static nk_bool show_menu = nk_true;
     static nk_flags window_flags = NK_WINDOW_TITLE|NK_WINDOW_BORDER|NK_WINDOW_SCALABLE|NK_WINDOW_MOVABLE|NK_WINDOW_MINIMIZABLE;
     nk_flags actual_window_flags;
 
     /* popups */
     static enum nk_style_header_align header_align = NK_HEADER_RIGHT;
-    static int show_app_about = nk_false;
+    static nk_bool show_app_about = nk_false;
 
     ctx->style.window.header.align = header_align;
 
+	static nk_bool disable_widgets = nk_false;
+
     actual_window_flags = window_flags;
     if (!(actual_window_flags & NK_WINDOW_TITLE))
         actual_window_flags &= ~(NK_WINDOW_MINIMIZABLE|NK_WINDOW_CLOSABLE);
@@ -23,7 +25,7 @@ overview(struct nk_context *ctx)
             enum menu_states {MENU_DEFAULT, MENU_WINDOWS};
             static nk_size mprog = 60;
             static int mslider = 10;
-            static int mcheck = nk_true;
+            static nk_bool mcheck = nk_true;
             nk_menubar_begin(ctx);
 
             /* menu #1 */
@@ -33,7 +35,7 @@ overview(struct nk_context *ctx)
             {
                 static size_t prog = 40;
                 static int slider = 10;
-                static int check = nk_true;
+                static nk_bool check = nk_true;
                 nk_layout_row_dynamic(ctx, 25, 1);
                 if (nk_menu_item_label(ctx, "Hide", NK_TEXT_LEFT))
                     show_menu = nk_false;
@@ -129,14 +131,22 @@ overview(struct nk_context *ctx)
             nk_checkbox_flags_label(ctx, "No Scrollbar", &window_flags, NK_WINDOW_NO_SCROLLBAR);
             nk_checkbox_flags_label(ctx, "Minimizable", &window_flags, NK_WINDOW_MINIMIZABLE);
             nk_checkbox_flags_label(ctx, "Scale Left", &window_flags, NK_WINDOW_SCALE_LEFT);
+			nk_checkbox_label(ctx, "Disable widgets", &disable_widgets);
             nk_tree_pop(ctx);
         }
 
+        if (disable_widgets)
+        	nk_widget_disable_begin(ctx);
+
         if (nk_tree_push(ctx, NK_TREE_TAB, "Widgets", NK_MINIMIZED))
         {
             enum options {A,B,C};
-            static int checkbox;
-            static int option;
+            static nk_bool checkbox_left_text_left;
+            static nk_bool checkbox_centered_text_right;
+            static nk_bool checkbox_right_text_right;
+            static nk_bool checkbox_right_text_left;
+            static int option_left;
+            static int option_right;
             if (nk_tree_push(ctx, NK_TREE_NODE, "Text", NK_MINIMIZED))
             {
                 /* Text Widgets */
@@ -180,6 +190,7 @@ overview(struct nk_context *ctx)
                 nk_layout_row_static(ctx, 30, 100, 2);
                 nk_button_symbol_label(ctx, NK_SYMBOL_TRIANGLE_LEFT, "prev", NK_TEXT_RIGHT);
                 nk_button_symbol_label(ctx, NK_SYMBOL_TRIANGLE_RIGHT, "next", NK_TEXT_LEFT);
+
                 nk_tree_pop(ctx);
             }
 
@@ -201,13 +212,21 @@ overview(struct nk_context *ctx)
                 static int range_int_max = 4096;
                 static const float ratio[] = {120, 150};
 
-                nk_layout_row_static(ctx, 30, 100, 1);
-                nk_checkbox_label(ctx, "Checkbox", &checkbox);
+                nk_layout_row_dynamic(ctx, 0, 1);
+                nk_checkbox_label(ctx, "CheckLeft TextLeft", &checkbox_left_text_left);
+                nk_checkbox_label_align(ctx, "CheckCenter TextRight", &checkbox_centered_text_right, NK_WIDGET_ALIGN_CENTERED | NK_WIDGET_ALIGN_MIDDLE, NK_TEXT_RIGHT);
+                nk_checkbox_label_align(ctx, "CheckRight TextRight", &checkbox_right_text_right, NK_WIDGET_LEFT, NK_TEXT_RIGHT);
+                nk_checkbox_label_align(ctx, "CheckRight TextLeft", &checkbox_right_text_left, NK_WIDGET_RIGHT, NK_TEXT_LEFT);
 
                 nk_layout_row_static(ctx, 30, 80, 3);
-                option = nk_option_label(ctx, "optionA", option == A) ? A : option;
-                option = nk_option_label(ctx, "optionB", option == B) ? B : option;
-                option = nk_option_label(ctx, "optionC", option == C) ? C : option;
+                option_left = nk_option_label(ctx, "optionA", option_left == A) ? A : option_left;
+                option_left = nk_option_label(ctx, "optionB", option_left == B) ? B : option_left;
+                option_left = nk_option_label(ctx, "optionC", option_left == C) ? C : option_left;
+
+                nk_layout_row_static(ctx, 30, 80, 3);
+                option_right = nk_option_label_align(ctx, "optionA", option_right == A, NK_WIDGET_RIGHT, NK_TEXT_RIGHT) ? A : option_right;
+                option_right = nk_option_label_align(ctx, "optionB", option_right == B, NK_WIDGET_RIGHT, NK_TEXT_RIGHT) ? B : option_right;
+                option_right = nk_option_label_align(ctx, "optionC", option_right == C, NK_WIDGET_RIGHT, NK_TEXT_RIGHT) ? C : option_right;
 
                 nk_layout_row(ctx, NK_STATIC, 30, 2, ratio);
                 nk_labelf(ctx, NK_TEXT_LEFT, "Slider int");
@@ -242,35 +261,28 @@ overview(struct nk_context *ctx)
 
             if (nk_tree_push(ctx, NK_TREE_NODE, "Inactive", NK_MINIMIZED))
             {
-                static int inactive = 1;
+                static nk_bool inactive = 1;
                 nk_layout_row_dynamic(ctx, 30, 1);
                 nk_checkbox_label(ctx, "Inactive", &inactive);
 
                 nk_layout_row_static(ctx, 30, 80, 1);
                 if (inactive) {
-                    struct nk_style_button button;
-                    button = ctx->style.button;
-                    ctx->style.button.normal = nk_style_item_color(nk_rgb(40,40,40));
-                    ctx->style.button.hover = nk_style_item_color(nk_rgb(40,40,40));
-                    ctx->style.button.active = nk_style_item_color(nk_rgb(40,40,40));
-                    ctx->style.button.border_color = nk_rgb(60,60,60);
-                    ctx->style.button.text_background = nk_rgb(60,60,60);
-                    ctx->style.button.text_normal = nk_rgb(60,60,60);
-                    ctx->style.button.text_hover = nk_rgb(60,60,60);
-                    ctx->style.button.text_active = nk_rgb(60,60,60);
-                    nk_button_label(ctx, "button");
-                    ctx->style.button = button;
-                } else if (nk_button_label(ctx, "button"))
+                    nk_widget_disable_begin(ctx);
+                }
+
+                if (nk_button_label(ctx, "button"))
                     fprintf(stdout, "button pressed\n");
+
+                nk_widget_disable_end(ctx);
+
                 nk_tree_pop(ctx);
             }
 
-
             if (nk_tree_push(ctx, NK_TREE_NODE, "Selectable", NK_MINIMIZED))
             {
                 if (nk_tree_push(ctx, NK_TREE_NODE, "List", NK_MINIMIZED))
                 {
-                    static int selected[4] = {nk_false, nk_false, nk_true, nk_false};
+                    static nk_bool selected[4] = {nk_false, nk_false, nk_true, nk_false};
                     nk_layout_row_static(ctx, 18, 100, 1);
                     nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[0]);
                     nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[1]);
@@ -282,7 +294,7 @@ overview(struct nk_context *ctx)
                 if (nk_tree_push(ctx, NK_TREE_NODE, "Grid", NK_MINIMIZED))
                 {
                     int i;
-                    static int selected[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
+                    static nk_bool selected[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
                     nk_layout_row_static(ctx, 50, 50, 4);
                     for (i = 0; i < 16; ++i) {
                         if (nk_selectable_label(ctx, "Z", NK_TEXT_CENTERED, &selected[i])) {
@@ -329,7 +341,7 @@ overview(struct nk_context *ctx)
                  */
                 static float chart_selection = 8.0f;
                 static int current_weapon = 0;
-                static int check_values[5];
+                static nk_bool check_values[5];
                 static float position[3];
                 static struct nk_color combo_color = {130, 50, 50, 255};
                 static struct nk_colorf combo_color2 = {0.509f, 0.705f, 0.2f, 1.0f};
@@ -590,7 +602,7 @@ overview(struct nk_context *ctx)
                 }
                 nk_tree_pop(ctx);
             }
-            
+
             if (nk_tree_push(ctx, NK_TREE_NODE, "Horizontal Rule", NK_MINIMIZED))
             {
                 nk_layout_row_dynamic(ctx, 12, 1);
@@ -620,6 +632,7 @@ overview(struct nk_context *ctx)
             float id = 0;
             static int col_index = -1;
             static int line_index = -1;
+            static int show_markers = nk_true;
             float step = (2*3.141592654f) / 32;
 
             int i;
@@ -628,7 +641,10 @@ overview(struct nk_context *ctx)
             /* line chart */
             id = 0;
             index = -1;
+            nk_layout_row_dynamic(ctx, 15, 1);
+            nk_checkbox_label(ctx, "Show markers", &show_markers);
             nk_layout_row_dynamic(ctx, 100, 1);
+            ctx->style.chart.show_markers = show_markers;
             if (nk_chart_begin(ctx, NK_CHART_LINES, 32, -1.0f, 1.0f)) {
                 for (i = 0; i < 32; ++i) {
                     nk_flags res = nk_chart_push(ctx, (float)cos(id));
@@ -701,8 +717,8 @@ overview(struct nk_context *ctx)
         if (nk_tree_push(ctx, NK_TREE_TAB, "Popup", NK_MINIMIZED))
         {
             static struct nk_color color = {255,0,0, 255};
-            static int select[4];
-            static int popup_active;
+            static nk_bool select[4];
+            static nk_bool popup_active;
             const struct nk_input *in = &ctx->input;
             struct nk_rect bounds;
 
@@ -871,9 +887,9 @@ overview(struct nk_context *ctx)
 
             if (nk_tree_push(ctx, NK_TREE_NODE, "Group", NK_MINIMIZED))
             {
-                static int group_titlebar = nk_false;
-                static int group_border = nk_true;
-                static int group_no_scrollbar = nk_false;
+                static nk_bool group_titlebar = nk_false;
+                static nk_bool group_border = nk_true;
+                static nk_bool group_no_scrollbar = nk_false;
                 static int group_width = 320;
                 static int group_height = 200;
 
@@ -899,7 +915,7 @@ overview(struct nk_context *ctx)
                 nk_layout_row_static(ctx, (float)group_height, group_width, 2);
                 if (nk_group_begin(ctx, "Group", group_flags)) {
                     int i = 0;
-                    static int selected[16];
+                    static nk_bool selected[16];
                     nk_layout_row_static(ctx, 18, 100, 1);
                     for (i = 0; i < 16; ++i)
                         nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]);
@@ -909,11 +925,12 @@ overview(struct nk_context *ctx)
             }
             if (nk_tree_push(ctx, NK_TREE_NODE, "Tree", NK_MINIMIZED))
             {
-                static int root_selected = 0;
-                int sel = root_selected;
+                static nk_bool root_selected = 0;
+                nk_bool sel = root_selected;
                 if (nk_tree_element_push(ctx, NK_TREE_NODE, "Root", NK_MINIMIZED, &sel)) {
-                    static int selected[8];
-                    int i = 0, node_select = selected[0];
+                    static nk_bool selected[8];
+                    int i = 0;
+                    nk_bool node_select = selected[0];
                     if (sel != root_selected) {
                         root_selected = sel;
                         for (i = 0; i < 8; ++i)
@@ -921,7 +938,7 @@ overview(struct nk_context *ctx)
                     }
                     if (nk_tree_element_push(ctx, NK_TREE_NODE, "Node", NK_MINIMIZED, &node_select)) {
                         int j = 0;
-                        static int sel_nodes[4];
+                        static nk_bool sel_nodes[4];
                         if (node_select != selected[0]) {
                             selected[0] = node_select;
                             for (i = 0; i < 4; ++i)
@@ -1049,7 +1066,7 @@ overview(struct nk_context *ctx)
                 nk_layout_space_begin(ctx, NK_STATIC, 500, 64);
                 nk_layout_space_push(ctx, nk_rect(0,0,150,500));
                 if (nk_group_begin(ctx, "Group_left", NK_WINDOW_BORDER)) {
-                    static int selected[32];
+                    static nk_bool selected[32];
                     nk_layout_row_static(ctx, 18, 100, 1);
                     for (i = 0; i < 32; ++i)
                         nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]);
@@ -1082,7 +1099,7 @@ overview(struct nk_context *ctx)
 
                 nk_layout_space_push(ctx, nk_rect(320,0,150,150));
                 if (nk_group_begin(ctx, "Group_right_top", NK_WINDOW_BORDER)) {
-                    static int selected[4];
+                    static nk_bool selected[4];
                     nk_layout_row_static(ctx, 18, 100, 1);
                     for (i = 0; i < 4; ++i)
                         nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]);
@@ -1091,7 +1108,7 @@ overview(struct nk_context *ctx)
 
                 nk_layout_space_push(ctx, nk_rect(320,160,150,150));
                 if (nk_group_begin(ctx, "Group_right_center", NK_WINDOW_BORDER)) {
-                    static int selected[4];
+                    static nk_bool selected[4];
                     nk_layout_row_static(ctx, 18, 100, 1);
                     for (i = 0; i < 4; ++i)
                         nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]);
@@ -1100,7 +1117,7 @@ overview(struct nk_context *ctx)
 
                 nk_layout_space_push(ctx, nk_rect(320,320,150,150));
                 if (nk_group_begin(ctx, "Group_right_bottom", NK_WINDOW_BORDER)) {
-                    static int selected[4];
+                    static nk_bool selected[4];
                     nk_layout_row_static(ctx, 18, 100, 1);
                     for (i = 0; i < 4; ++i)
                         nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]);
@@ -1289,8 +1306,9 @@ overview(struct nk_context *ctx)
             }
             nk_tree_pop(ctx);
         }
+        if (disable_widgets)
+     		nk_widget_disable_end(ctx);
     }
     nk_end(ctx);
     return !nk_window_is_closed(ctx, "Overview");
 }
-

+ 1 - 0
demo/d3d11/nuklear_d3d11.h

@@ -41,6 +41,7 @@ NK_API void nk_d3d11_shutdown(void);
 #define COBJMACROS
 #include <d3d11.h>
 
+#include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #include <float.h>

+ 1 - 0
demo/d3d12/nuklear_d3d12.h

@@ -88,6 +88,7 @@ NK_API void nk_d3d12_shutdown(void);
 #define COBJMACROS
 #include <d3d12.h>
 
+#include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #include <float.h>

+ 1 - 0
demo/d3d9/nuklear_d3d9.h

@@ -41,6 +41,7 @@ NK_API void nk_d3d9_shutdown(void);
 #define COBJMACROS
 #include <d3d9.h>
 
+#include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 

+ 1 - 1
demo/gdi/nuklear_gdi.h

@@ -38,7 +38,7 @@ NK_API void nk_gdi_set_font(GdiFont *font);
  * ===============================================================
  */
 #ifdef NK_GDI_IMPLEMENTATION
-
+#include <string.h>
 #include <stdlib.h>
 #include <malloc.h>
 

+ 1 - 1
demo/gdi_native_nuklear/nuklear_gdi.h

@@ -50,7 +50,7 @@ NK_API void nk_gdi_set_font(nk_gdi_ctx gdi, GdiFont* font);
  * ===============================================================
  */
 #ifdef NK_GDI_IMPLEMENTATION
-
+#include <string.h>
 #include <stdlib.h>
 #include <malloc.h>
 

+ 2 - 0
demo/glfw_opengl2/nuklear_glfw_gl2.h

@@ -40,6 +40,8 @@ NK_API void                 nk_gflw3_scroll_callback(GLFWwindow *win, double xof
  * ===============================================================
  */
 #ifdef NK_GLFW_GL2_IMPLEMENTATION
+#include <string.h>
+#include <stdlib.h>
 
 #ifndef NK_GLFW_TEXT_MAX
 #define NK_GLFW_TEXT_MAX 256

+ 3 - 0
demo/glfw_opengl3/nuklear_glfw_gl3.h

@@ -78,6 +78,9 @@ NK_API void                 nk_glfw3_mouse_button_callback(GLFWwindow *win, int
  * ===============================================================
  */
 #ifdef NK_GLFW_GL3_IMPLEMENTATION
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
 
 #ifndef NK_GLFW_DOUBLE_CLICK_LO
 #define NK_GLFW_DOUBLE_CLICK_LO 0.02

+ 3 - 0
demo/glfw_opengl4/nuklear_glfw_gl4.h

@@ -50,6 +50,9 @@ NK_API void                 nk_glfw3_destroy_texture(int tex_index);
  */
 #ifdef NK_GLFW_GL4_IMPLEMENTATION
 #undef NK_GLFW_GL4_IMPLEMENTATION
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
 
 #ifndef NK_GLFW_TEXT_MAX
 #define NK_GLFW_TEXT_MAX 256

+ 4 - 0
demo/glfw_vulkan/.clang-format

@@ -0,0 +1,4 @@
+---
+BasedOnStyle: LLVM
+# same as .editorconfig
+IndentWidth: 4

+ 3 - 0
demo/glfw_vulkan/.gitignore

@@ -0,0 +1,3 @@
+src/nuklearshaders/*.spv
+src/nuklear_glfw_vulkan.h
+shaders/*.spv

+ 29 - 0
demo/glfw_vulkan/Makefile

@@ -0,0 +1,29 @@
+# Install
+BIN = demo
+
+# Flags
+CFLAGS += -std=c89 -Wall -Wextra -pedantic -O2
+
+SRC = main.c
+OBJ = $(SRC:.c=.o)
+
+ifeq ($(OS),Windows_NT)
+BIN := $(BIN).exe
+LIBS = -lglfw3 -lvulkan -lm
+else
+	UNAME_S := $(shell uname -s)
+	GLFW3 := $(shell pkg-config --libs glfw3)
+    LIBS = $(GLFW3) -lvulkan -lm
+endif
+
+
+$(BIN): shaders/demo.vert.spv shaders/demo.frag.spv
+	@mkdir -p bin
+	rm -f bin/$(BIN) $(OBJS)
+	$(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) $(LIBS)
+
+shaders/demo.vert.spv: shaders/demo.vert
+	glslc --target-env=vulkan shaders/demo.vert -o shaders/demo.vert.spv
+
+shaders/demo.frag.spv: shaders/demo.frag
+	glslc --target-env=vulkan shaders/demo.frag -o shaders/demo.frag.spv

+ 52 - 0
demo/glfw_vulkan/README.md

@@ -0,0 +1,52 @@
+# nuklear glfw vulkan
+
+## Theory of operation
+
+The nuklear glfw vulkan integration creates an independent graphics pipeline that will render the nuklear UI to separate render targets.
+The application is responsible to fully manage these render targets. So it must ensure they are properly sized (and resized when requested).
+
+Furthermore it is assumed that you will have a swap chain in place and the number of nuklear overlay images and number of swap chain images match.
+
+This is how you can integrate it in your application:
+
+```
+/*
+Setup: overlay_images have been created and their number match with the number
+of the swap_chain_images of your application. The overlay_images in this
+example have the same format as your swap_chain images (optional)
+*/
+    struct nk_context *ctx = nk_glfw3_init(
+        demo.win, demo.device, demo.physical_device, demo.indices.graphics,
+        demo.overlay_image_views, demo.swap_chain_images_len,
+        demo.swap_chain_image_format, NK_GLFW3_INSTALL_CALLBACKS,
+        MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER);
+[...]
+/*
+in your draw loop draw you can then render to the overlay image at
+`image_index`
+your own application can then wait for the semaphore and produce
+the swap_chain_image at `image_index`
+this should simply sample from the overlay_image (see example)
+*/
+nk_semaphore semaphore =
+    nk_glfw3_render(demo.graphics_queue, image_index,
+                    demo.image_available, NK_ANTI_ALIASING_ON);
+    if (!render(&demo, &bg, nk_semaphore, image_index)) {
+        fprintf(stderr, "render failed\n");
+        return false;
+    }
+```
+
+You must call `nk_glfw3_resize` whenever the size of the overlay_images resize.
+
+## Using images
+
+Images can be used by providing a VkImageView as an nk_image_ptr to nuklear:
+
+```
+img = nk_image_ptr(demo.demo_texture_image_view);
+```
+
+Note that they must have SHADER_READ_OPTIMAL layout
+
+It is currently not possible to specify how they are being sampled. The nuklear glfw vulkan integration uses a fixed sampler that does linear filtering.

+ 2229 - 0
demo/glfw_vulkan/main.c

@@ -0,0 +1,2229 @@
+/* nuklear - 1.32.0 - public domain */
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+#define GLFW_INCLUDE_VULKAN
+#include <GLFW/glfw3.h>
+
+#define NK_INCLUDE_FIXED_TYPES
+#define NK_INCLUDE_STANDARD_IO
+#define NK_INCLUDE_STANDARD_VARARGS
+#define NK_INCLUDE_DEFAULT_ALLOCATOR
+#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+#define NK_INCLUDE_FONT_BAKING
+#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"
+
+#define WINDOW_WIDTH 1200
+#define WINDOW_HEIGHT 800
+
+#define MAX_VERTEX_BUFFER 512 * 1024
+#define MAX_ELEMENT_BUFFER 128 * 1024
+
+/* ===============================================================
+ *
+ *                          EXAMPLE
+ *
+ * ===============================================================*/
+/* This are some code examples to provide a small overview of what can be
+ * done with this library. To try out an example uncomment the defines */
+#define INCLUDE_ALL
+/*#define INCLUDE_STYLE */
+/*#define INCLUDE_CALCULATOR */
+/*#define INCLUDE_CANVAS */
+/*#define INCLUDE_OVERVIEW*/
+/*#define INCLUDE_NODE_EDITOR */
+
+#ifdef INCLUDE_ALL
+#define INCLUDE_STYLE
+#define INCLUDE_CALCULATOR
+#define INCLUDE_CANVAS
+#define INCLUDE_OVERVIEW
+#define INCLUDE_NODE_EDITOR
+#endif
+
+#ifdef INCLUDE_STYLE
+#include "../../demo/common/style.c"
+#endif
+#ifdef INCLUDE_CALCULATOR
+#include "../../demo/common/calculator.c"
+#endif
+#ifdef INCLUDE_CANVAS
+#include "../../demo/common/canvas.c"
+#endif
+#ifdef INCLUDE_OVERVIEW
+#include "../../demo/common/overview.c"
+#endif
+#ifdef INCLUDE_NODE_EDITOR
+#include "../../demo/common/node_editor.c"
+#endif
+
+/* ===============================================================
+ *
+ *                          DEMO
+ *
+ * ===============================================================*/
+
+static const char *validation_layer_name = "VK_LAYER_KHRONOS_validation";
+
+struct queue_family_indices {
+    int graphics;
+    int present;
+};
+
+struct swap_chain_support_details {
+    VkSurfaceCapabilitiesKHR capabilities;
+    VkSurfaceFormatKHR *formats;
+    uint32_t formats_len;
+    VkPresentModeKHR *present_modes;
+    uint32_t present_modes_len;
+};
+
+void swap_chain_support_details_free(
+    struct swap_chain_support_details *swap_chain_support) {
+    if (swap_chain_support->formats_len > 0) {
+        free(swap_chain_support->formats);
+        swap_chain_support->formats = NULL;
+    }
+    if (swap_chain_support->present_modes_len > 0) {
+        free(swap_chain_support->present_modes);
+        swap_chain_support->present_modes = NULL;
+    }
+}
+
+struct vulkan_demo {
+    GLFWwindow *win;
+    VkInstance instance;
+    VkDebugUtilsMessengerEXT debug_messenger;
+    VkSurfaceKHR surface;
+    VkPhysicalDevice physical_device;
+    struct queue_family_indices indices;
+    VkDevice device;
+    VkQueue graphics_queue;
+    VkQueue present_queue;
+    VkSampler sampler;
+
+    VkSwapchainKHR swap_chain;
+    VkImage *swap_chain_images;
+    uint32_t swap_chain_images_len;
+    VkImageView *swap_chain_image_views;
+    VkFormat swap_chain_image_format;
+    VkExtent2D swap_chain_image_extent;
+
+    VkImage *overlay_images;
+    VkImageView *overlay_image_views;
+    VkDeviceMemory *overlay_image_memories;
+
+    VkRenderPass render_pass;
+    VkFramebuffer *framebuffers;
+    VkDescriptorSetLayout descriptor_set_layout;
+    VkDescriptorPool descriptor_pool;
+    VkDescriptorSet *descriptor_sets;
+    VkPipelineLayout pipeline_layout;
+    VkPipeline pipeline;
+    VkCommandPool command_pool;
+    VkCommandBuffer *command_buffers;
+    VkSemaphore image_available;
+    VkSemaphore render_finished;
+
+    VkImage demo_texture_image;
+    VkImageView demo_texture_image_view;
+    VkDeviceMemory demo_texture_memory;
+
+    VkFence render_fence;
+};
+
+static void glfw_error_callback(int e, const char *d) {
+    fprintf(stderr, "Error %d: %s\n", e, d);
+}
+
+VKAPI_ATTR VkBool32 VKAPI_CALL
+vulkan_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
+                      VkDebugUtilsMessageTypeFlagsEXT message_type,
+                      const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
+                      void *user_data) {
+    (void)message_severity;
+    (void)message_type;
+    (void)user_data;
+    fprintf(stderr, "validation layer: %s\n", callback_data->pMessage);
+
+    return VK_FALSE;
+}
+
+bool check_validation_layer_support() {
+    uint32_t layer_count;
+    bool ret = false;
+    VkResult result;
+    uint32_t i;
+    VkLayerProperties *available_layers = NULL;
+
+    result = vkEnumerateInstanceLayerProperties(&layer_count, NULL);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEnumerateInstanceLayerProperties failed: %d\n",
+                result);
+        return ret;
+    }
+
+    available_layers = malloc(layer_count * sizeof(VkLayerProperties));
+    result = vkEnumerateInstanceLayerProperties(&layer_count, available_layers);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEnumerateInstanceLayerProperties failed: %d\n",
+                result);
+        goto cleanup;
+    }
+
+    printf("Available vulkan layers:\n");
+    for (i = 0; i < layer_count; i++) {
+        printf("  %s\n", available_layers[i].layerName);
+        if (strcmp(validation_layer_name, available_layers[i].layerName) == 0) {
+            ret = true;
+            break;
+        }
+    }
+cleanup:
+    free(available_layers);
+    return ret;
+}
+
+VkResult create_debug_utils_messenger_ext(
+    VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo,
+    const VkAllocationCallbacks *pAllocator,
+    VkDebugUtilsMessengerEXT *pDebugMessenger) {
+    PFN_vkCreateDebugUtilsMessengerEXT func =
+        (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
+            instance, "vkCreateDebugUtilsMessengerEXT");
+    if (func != NULL) {
+        return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
+    } else {
+        return VK_ERROR_EXTENSION_NOT_PRESENT;
+    }
+}
+
+bool create_debug_callback(struct vulkan_demo *demo) {
+    VkResult result;
+
+    VkDebugUtilsMessengerCreateInfoEXT create_info;
+    memset(&create_info, 0, sizeof(VkDebugUtilsMessengerCreateInfoEXT));
+    create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
+    create_info.messageSeverity =
+        VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
+        VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+    create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
+                              VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
+                              VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
+    create_info.pfnUserCallback = vulkan_debug_callback;
+
+    result = create_debug_utils_messenger_ext(demo->instance, &create_info,
+                                              NULL, &demo->debug_messenger);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "create_debug_utils_messenger_ext failed %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+bool create_instance(struct vulkan_demo *demo) {
+    uint32_t i;
+    uint32_t available_instance_extension_count;
+    VkResult result;
+    VkExtensionProperties *available_instance_extensions = NULL;
+    bool ret = false;
+    VkApplicationInfo app_info;
+    VkInstanceCreateInfo create_info;
+    uint32_t glfw_extension_count;
+    const char **glfw_extensions;
+    uint32_t enabled_extension_count;
+    const char **enabled_extensions = NULL;
+    bool validation_layers_installed;
+
+    validation_layers_installed = check_validation_layer_support();
+
+    if (!validation_layers_installed) {
+        fprintf(stdout,
+                "Couldn't find validation layer %s. Continuing without "
+                "validation layers.\n",
+                validation_layer_name);
+    }
+    result = vkEnumerateInstanceExtensionProperties(
+        NULL, &available_instance_extension_count, NULL);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEnumerateInstanceExtensionProperties failed %d\n",
+                result);
+        return ret;
+    }
+
+    available_instance_extensions = malloc(available_instance_extension_count *
+                                           sizeof(VkExtensionProperties));
+
+    result = vkEnumerateInstanceExtensionProperties(
+        NULL, &available_instance_extension_count,
+        available_instance_extensions);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEnumerateInstanceExtensionProperties failed %d\n",
+                result);
+        goto cleanup;
+    }
+
+    printf("available instance extensions:\n");
+    for (i = 0; i < available_instance_extension_count; i++) {
+        printf("  %s\n", available_instance_extensions[i].extensionName);
+    }
+
+    glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count);
+
+    enabled_extension_count = glfw_extension_count;
+    if (validation_layers_installed) {
+        enabled_extension_count += 1;
+        enabled_extensions = malloc(enabled_extension_count * sizeof(char *));
+        memcpy(enabled_extensions, glfw_extensions,
+               glfw_extension_count * sizeof(char *));
+        enabled_extensions[glfw_extension_count] =
+            VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
+    } else {
+        enabled_extensions = malloc(enabled_extension_count * sizeof(char *));
+        memcpy(enabled_extensions, glfw_extensions,
+               glfw_extension_count * sizeof(char *));
+    }
+
+    printf("Trying to enable the following instance extensions: ");
+    for (i = 0; i < enabled_extension_count; i++) {
+        if (i > 0) {
+            printf(", ");
+        }
+        printf(enabled_extensions[i]);
+    }
+    printf("\n");
+    for (i = 0; i < enabled_extension_count; i++) {
+        int extension_missing = 1;
+        uint32_t j;
+        for (j = 0; j < available_instance_extension_count; j++) {
+            if (strcmp(enabled_extensions[i],
+                       available_instance_extensions[j].extensionName) == 0) {
+                extension_missing = 0;
+                break;
+            }
+        }
+        if (extension_missing) {
+            fprintf(stderr, "Extension %s is missing\n", enabled_extensions[i]);
+            return ret;
+        }
+    }
+
+    memset(&app_info, 0, sizeof(VkApplicationInfo));
+    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+    app_info.pApplicationName = "Demo";
+    app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
+    app_info.pEngineName = "No Engine";
+    app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
+    app_info.apiVersion = VK_API_VERSION_1_0;
+
+    memset(&create_info, 0, sizeof(VkInstanceCreateInfo));
+    create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+    create_info.pApplicationInfo = &app_info;
+    create_info.enabledExtensionCount = enabled_extension_count;
+    create_info.ppEnabledExtensionNames = enabled_extensions;
+    if (validation_layers_installed) {
+        create_info.enabledLayerCount = 1;
+        create_info.ppEnabledLayerNames = &validation_layer_name;
+    }
+    result = vkCreateInstance(&create_info, NULL, &demo->instance);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateInstance result %d\n", result);
+        return ret;
+    }
+    if (validation_layers_installed) {
+        ret = create_debug_callback(demo);
+    } else {
+        ret = true;
+    }
+cleanup:
+    if (available_instance_extensions) {
+        free(available_instance_extensions);
+    }
+    if (enabled_extensions) {
+        free(enabled_extensions);
+    }
+
+    return ret;
+}
+
+bool create_surface(struct vulkan_demo *demo) {
+    VkResult result;
+    result = glfwCreateWindowSurface(demo->instance, demo->win, NULL,
+                                     &demo->surface);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "creating vulkan surface failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+bool find_queue_families(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                         struct queue_family_indices *indices) {
+    VkResult result;
+    uint32_t queue_family_count = 0;
+    uint32_t i = 0;
+    bool ret = false;
+    VkQueueFamilyProperties *queue_family_properties;
+    VkBool32 present_support;
+
+    vkGetPhysicalDeviceQueueFamilyProperties(physical_device,
+                                             &queue_family_count, NULL);
+
+    queue_family_properties =
+        malloc(queue_family_count * sizeof(VkQueueFamilyProperties));
+    vkGetPhysicalDeviceQueueFamilyProperties(
+        physical_device, &queue_family_count, queue_family_properties);
+
+    for (i = 0; i < queue_family_count; i++) {
+        if (queue_family_properties[i].queueCount == 0) {
+            continue;
+        }
+        if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+            indices->graphics = i;
+        }
+
+        result = vkGetPhysicalDeviceSurfaceSupportKHR(
+            physical_device, i, surface, &present_support);
+        if (result != VK_SUCCESS) {
+            fprintf(stderr,
+                    "vkGetPhysicalDeviceSurfaceSupportKHR failed with %d\n",
+                    result);
+            goto cleanup;
+        }
+        if (present_support == VK_TRUE) {
+            indices->present = i;
+        }
+        if (indices->graphics >= 0 && indices->present >= 0) {
+            break;
+        }
+    }
+    ret = true;
+cleanup:
+    free(queue_family_properties);
+    return ret;
+}
+
+bool query_swap_chain_support(
+    VkPhysicalDevice device, VkSurfaceKHR surface,
+    struct swap_chain_support_details *swap_chain_support) {
+    VkResult result;
+
+    result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+        device, surface, &swap_chain_support->capabilities);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr,
+                "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: %d\n",
+                result);
+        return false;
+    }
+
+    result = vkGetPhysicalDeviceSurfaceFormatsKHR(
+        device, surface, &swap_chain_support->formats_len, NULL);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: %d\n",
+                result);
+        return false;
+    }
+
+    if (swap_chain_support->formats_len != 0) {
+        swap_chain_support->formats = malloc(swap_chain_support->formats_len *
+                                             sizeof(VkSurfaceFormatKHR));
+        result = vkGetPhysicalDeviceSurfaceFormatsKHR(
+            device, surface, &swap_chain_support->formats_len,
+            swap_chain_support->formats);
+
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: %d\n",
+                    result);
+            return false;
+        }
+    }
+
+    result = vkGetPhysicalDeviceSurfacePresentModesKHR(
+        device, surface, &swap_chain_support->present_modes_len, NULL);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr,
+                "vkGetPhysicalDeviceSurfacePresentModesKHR failed: %d\n",
+                result);
+        return false;
+    }
+
+    if (swap_chain_support->present_modes_len != 0) {
+        swap_chain_support->present_modes = malloc(
+            swap_chain_support->present_modes_len * sizeof(VkPresentModeKHR));
+        result = vkGetPhysicalDeviceSurfacePresentModesKHR(
+            device, surface, &swap_chain_support->present_modes_len,
+            swap_chain_support->present_modes);
+
+        if (result != VK_SUCCESS) {
+            fprintf(stderr,
+                    "vkGetPhysicalDeviceSurfacePresentModesKHR failed: %d\n",
+                    result);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool is_suitable_physical_device(VkPhysicalDevice physical_device,
+                                 VkSurfaceKHR surface,
+                                 struct queue_family_indices *indices) {
+    VkResult result;
+    uint32_t device_extension_count;
+    uint32_t i;
+    VkExtensionProperties *device_extensions;
+    bool ret = false;
+    struct swap_chain_support_details swap_chain_support;
+    int found_khr_surface = 0;
+
+    VkPhysicalDeviceProperties device_properties;
+    vkGetPhysicalDeviceProperties(physical_device, &device_properties);
+
+    printf("Probing physical device %s\n", device_properties.deviceName);
+
+    result = vkEnumerateDeviceExtensionProperties(
+        physical_device, NULL, &device_extension_count, NULL);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEnumerateDeviceExtensionProperties failed: %d\n",
+                result);
+        return false;
+    }
+
+    device_extensions =
+        malloc(device_extension_count * sizeof(VkExtensionProperties));
+
+    result = vkEnumerateDeviceExtensionProperties(
+        physical_device, NULL, &device_extension_count, device_extensions);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEnumerateDeviceExtensionProperties failed: %d\n",
+                result);
+        goto cleanup;
+    }
+
+    printf("  Supported device extensions:\n");
+
+    for (i = 0; i < device_extension_count; i++) {
+        printf("    %s\n", device_extensions[i].extensionName);
+        if (strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+                   device_extensions[i].extensionName) == 0) {
+            found_khr_surface = 1;
+            break;
+        }
+    }
+    if (!found_khr_surface) {
+        printf("  Device doesnt support %s\n", VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+        goto cleanup;
+    }
+    if (!find_queue_families(physical_device, surface, indices)) {
+        goto cleanup;
+    }
+    if (indices->graphics < 0 || indices->present < 0) {
+        printf("  Device is missing graphics and/or present support. graphics: "
+               "%d, present: %d\n",
+               indices->graphics, indices->present);
+        goto cleanup;
+    }
+
+    if (!query_swap_chain_support(physical_device, surface,
+                                  &swap_chain_support)) {
+        goto cleanup;
+    }
+
+    if (swap_chain_support.formats_len == 0) {
+        printf(" Device doesn't support any swap chain formats\n");
+        goto cleanup;
+    }
+
+    if (swap_chain_support.present_modes_len == 0) {
+        printf(" Device doesn't support any swap chain present modes\n");
+        goto cleanup;
+    }
+    ret = true;
+
+cleanup:
+    free(device_extensions);
+    swap_chain_support_details_free(&swap_chain_support);
+
+    return ret;
+}
+
+bool create_physical_device(struct vulkan_demo *demo) {
+    uint32_t device_count = 0;
+    VkPhysicalDevice *physical_devices;
+    VkResult result;
+    uint32_t i;
+    bool ret = false;
+
+    result = vkEnumeratePhysicalDevices(demo->instance, &device_count, NULL);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEnumeratePhysicalDevices failed: %d\n", result);
+        return ret;
+    }
+    if (device_count == 0) {
+        fprintf(stderr, "no vulkan capable GPU found!");
+        return ret;
+    }
+
+    physical_devices = malloc(device_count * sizeof(VkPhysicalDevice));
+    result = vkEnumeratePhysicalDevices(demo->instance, &device_count,
+                                        physical_devices);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEnumeratePhysicalDevices failed: %d\n", result);
+        goto cleanup;
+    }
+
+    for (i = 0; i < device_count; i++) {
+        struct queue_family_indices indices = {-1, -1};
+        if (is_suitable_physical_device(physical_devices[i], demo->surface,
+                                        &indices)) {
+            printf("  Selecting this device for rendering. Queue families: "
+                   "graphics: %d, present: %d!\n",
+                   indices.graphics, indices.present);
+            demo->physical_device = physical_devices[i];
+            demo->indices = indices;
+            break;
+        }
+    }
+    if (demo->physical_device == NULL) {
+        fprintf(stderr, "failed to find a suitable GPU!\n");
+    } else {
+        ret = true;
+    }
+cleanup:
+    free(physical_devices);
+    return ret;
+}
+
+bool create_logical_device(struct vulkan_demo *demo) {
+    VkResult result;
+    bool ret = false;
+    float queuePriority = 1.0f;
+    uint32_t num_queues = 1;
+    VkDeviceQueueCreateInfo *queue_create_infos;
+    VkDeviceCreateInfo create_info;
+    const char *swap_chain_extension_name = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
+
+    queue_create_infos = calloc(2, sizeof(VkDeviceQueueCreateInfo));
+    queue_create_infos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+    queue_create_infos[0].queueFamilyIndex = demo->indices.graphics;
+    queue_create_infos[0].queueCount = 1;
+    queue_create_infos[0].pQueuePriorities = &queuePriority;
+
+    if (demo->indices.present != demo->indices.graphics) {
+        queue_create_infos[1].sType =
+            VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+        queue_create_infos[1].queueFamilyIndex = demo->indices.present;
+        queue_create_infos[1].queueCount = 1;
+        queue_create_infos[1].pQueuePriorities = &queuePriority;
+        num_queues = 2;
+    }
+
+    memset(&create_info, 0, sizeof(VkDeviceCreateInfo));
+    create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+    create_info.queueCreateInfoCount = num_queues;
+    create_info.pQueueCreateInfos = queue_create_infos;
+    create_info.enabledExtensionCount = 1;
+    create_info.ppEnabledExtensionNames = &swap_chain_extension_name;
+
+    result = vkCreateDevice(demo->physical_device, &create_info, NULL,
+                            &demo->device);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateDevice failed: %d\n", result);
+        goto cleanup;
+    }
+
+    vkGetDeviceQueue(demo->device, demo->indices.graphics, 0,
+                     &demo->graphics_queue);
+    vkGetDeviceQueue(demo->device, demo->indices.present, 0,
+                     &demo->present_queue);
+    ret = true;
+cleanup:
+    free(queue_create_infos);
+    return ret;
+}
+
+bool create_sampler(struct vulkan_demo *demo) {
+    VkResult result;
+    VkSamplerCreateInfo sampler_info;
+
+    memset(&sampler_info, 0, sizeof(VkSamplerCreateInfo));
+    sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+    sampler_info.pNext = NULL;
+    sampler_info.maxAnisotropy = 1.0;
+    sampler_info.magFilter = VK_FILTER_LINEAR;
+    sampler_info.minFilter = VK_FILTER_LINEAR;
+    sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+    sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.mipLodBias = 0.0f;
+    sampler_info.compareEnable = VK_FALSE;
+    sampler_info.compareOp = VK_COMPARE_OP_ALWAYS;
+    sampler_info.minLod = 0.0f;
+    sampler_info.maxLod = 0.0f;
+    sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
+
+    result = vkCreateSampler(demo->device, &sampler_info, NULL, &demo->sampler);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateSampler failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+VkSurfaceFormatKHR
+choose_swap_surface_format(VkSurfaceFormatKHR *available_formats,
+                           uint32_t available_formats_len) {
+    VkSurfaceFormatKHR undefined_format = {VK_FORMAT_B8G8R8A8_UNORM,
+                                           VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
+    uint32_t i;
+    if (available_formats_len == 1 &&
+        available_formats[0].format == VK_FORMAT_UNDEFINED) {
+        return undefined_format;
+    }
+
+    for (i = 0; i < available_formats_len; i++) {
+        if (available_formats[i].format == VK_FORMAT_B8G8R8A8_UNORM &&
+            available_formats[i].colorSpace ==
+                VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+            return available_formats[i];
+        }
+    }
+
+    return available_formats[0];
+}
+
+VkPresentModeKHR
+choose_swap_present_mode(VkPresentModeKHR *available_present_modes,
+                         uint32_t available_present_modes_len) {
+    uint32_t i;
+    for (i = 0; i < available_present_modes_len; i++) {
+        /*
+        best mode to ensure good input latency while ensuring we are not
+        producing tearing
+        */
+        if (available_present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
+            return available_present_modes[i];
+        }
+    }
+
+    /* must be supported */
+    return VK_PRESENT_MODE_FIFO_KHR;
+}
+
+VkExtent2D choose_swap_extent(struct vulkan_demo *demo,
+                              VkSurfaceCapabilitiesKHR *capabilities) {
+    int width, height;
+    VkExtent2D actual_extent;
+    if (capabilities->currentExtent.width != 0xFFFFFFFF) {
+        return capabilities->currentExtent;
+    } else {
+        /* not window size! */
+        glfwGetFramebufferSize(demo->win, &width, &height);
+
+        actual_extent.width = (uint32_t)width;
+        actual_extent.height = (uint32_t)height;
+
+        actual_extent.width = NK_MAX(
+            capabilities->minImageExtent.width,
+            NK_MIN(capabilities->maxImageExtent.width, actual_extent.width));
+        actual_extent.height = NK_MAX(
+            capabilities->minImageExtent.height,
+            NK_MIN(capabilities->maxImageExtent.height, actual_extent.height));
+
+        return actual_extent;
+    }
+}
+
+bool create_swap_chain(struct vulkan_demo *demo) {
+    struct swap_chain_support_details swap_chain_support;
+    VkSurfaceFormatKHR surface_format;
+    VkPresentModeKHR present_mode;
+    VkExtent2D extent;
+    VkResult result;
+    VkSwapchainCreateInfoKHR create_info;
+    uint32_t queue_family_indices[2];
+    bool ret = false;
+
+    queue_family_indices[0] = (uint32_t)demo->indices.graphics;
+    queue_family_indices[1] = (uint32_t)demo->indices.present;
+
+    if (!query_swap_chain_support(demo->physical_device, demo->surface,
+                                  &swap_chain_support)) {
+        goto cleanup;
+    }
+    surface_format = choose_swap_surface_format(swap_chain_support.formats,
+                                                swap_chain_support.formats_len);
+    present_mode = choose_swap_present_mode(
+        swap_chain_support.present_modes, swap_chain_support.present_modes_len);
+    extent = choose_swap_extent(demo, &swap_chain_support.capabilities);
+
+    demo->swap_chain_images_len =
+        swap_chain_support.capabilities.minImageCount + 1;
+    if (swap_chain_support.capabilities.maxImageCount > 0 &&
+        demo->swap_chain_images_len >
+            swap_chain_support.capabilities.maxImageCount) {
+        demo->swap_chain_images_len =
+            swap_chain_support.capabilities.maxImageCount;
+    }
+
+    memset(&create_info, 0, sizeof(VkSwapchainCreateInfoKHR));
+    create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+    create_info.surface = demo->surface;
+
+    create_info.minImageCount = demo->swap_chain_images_len;
+    create_info.imageFormat = surface_format.format;
+    create_info.imageColorSpace = surface_format.colorSpace;
+    create_info.imageExtent = extent;
+    create_info.imageArrayLayers = 1;
+    create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+
+    if (demo->indices.graphics != demo->indices.present) {
+        create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
+        create_info.queueFamilyIndexCount = 2;
+        create_info.pQueueFamilyIndices = queue_family_indices;
+    } else {
+        create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    }
+
+    create_info.preTransform = swap_chain_support.capabilities.currentTransform;
+    create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+    create_info.presentMode = present_mode;
+    create_info.clipped = VK_TRUE;
+
+    result = vkCreateSwapchainKHR(demo->device, &create_info, NULL,
+                                  &demo->swap_chain);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateSwapchainKHR failed: %d\n", result);
+        goto cleanup;
+    }
+
+    result = vkGetSwapchainImagesKHR(demo->device, demo->swap_chain,
+                                     &demo->swap_chain_images_len, NULL);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkGetSwapchainImagesKHR failed: %d\n", result);
+        goto cleanup;
+    }
+    if (demo->swap_chain_images == NULL) {
+        demo->swap_chain_images =
+            malloc(demo->swap_chain_images_len * sizeof(VkImage));
+    }
+    result = vkGetSwapchainImagesKHR(demo->device, demo->swap_chain,
+                                     &demo->swap_chain_images_len,
+                                     demo->swap_chain_images);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkGetSwapchainImagesKHR failed: %d\n", result);
+        return false;
+    }
+
+    demo->swap_chain_image_format = surface_format.format;
+    demo->swap_chain_image_extent = extent;
+
+    ret = true;
+cleanup:
+    swap_chain_support_details_free(&swap_chain_support);
+
+    return ret;
+}
+
+bool create_swap_chain_image_views(struct vulkan_demo *demo) {
+    uint32_t i;
+    VkResult result;
+    VkImageViewCreateInfo create_info;
+
+    memset(&create_info, 0, sizeof(VkImageViewCreateInfo));
+    create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    create_info.format = demo->swap_chain_image_format;
+    create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+    create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+    create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+    create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+    create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    create_info.subresourceRange.baseMipLevel = 0;
+    create_info.subresourceRange.levelCount = 1;
+    create_info.subresourceRange.baseArrayLayer = 0;
+    create_info.subresourceRange.layerCount = 1;
+
+    if (!demo->swap_chain_image_views) {
+        demo->swap_chain_image_views =
+            malloc(demo->swap_chain_images_len * sizeof(VkImageView));
+    }
+
+    for (i = 0; i < demo->swap_chain_images_len; i++) {
+        create_info.image = demo->swap_chain_images[i];
+        result = vkCreateImageView(demo->device, &create_info, NULL,
+                                   &demo->swap_chain_image_views[i]);
+
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "vkCreateImageView failed: %d\n", result);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool create_overlay_images(struct vulkan_demo *demo) {
+    uint32_t i, j;
+    VkResult result;
+    VkMemoryRequirements mem_requirements;
+    VkPhysicalDeviceMemoryProperties mem_properties;
+    int found;
+    VkImageCreateInfo image_info;
+    VkMemoryAllocateInfo alloc_info;
+    VkImageViewCreateInfo image_view_info;
+
+    memset(&image_info, 0, sizeof(VkImageCreateInfo));
+    image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    image_info.imageType = VK_IMAGE_TYPE_2D;
+    image_info.extent.width = demo->swap_chain_image_extent.width;
+    image_info.extent.height = demo->swap_chain_image_extent.height;
+    image_info.extent.depth = 1;
+    image_info.mipLevels = 1;
+    image_info.arrayLayers = 1;
+    image_info.format = demo->swap_chain_image_format;
+    image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+    image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    image_info.usage =
+        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    image_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+    memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo));
+    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+
+    memset(&image_view_info, 0, sizeof(VkImageViewCreateInfo));
+    image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    image_view_info.format = demo->swap_chain_image_format;
+    image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    image_view_info.subresourceRange.baseMipLevel = 0;
+    image_view_info.subresourceRange.levelCount = 1;
+    image_view_info.subresourceRange.baseArrayLayer = 0;
+    image_view_info.subresourceRange.layerCount = 1;
+
+    if (!demo->overlay_images) {
+        demo->overlay_images =
+            malloc(demo->swap_chain_images_len * sizeof(VkImage));
+    }
+
+    if (!demo->overlay_image_memories) {
+        demo->overlay_image_memories =
+            malloc(demo->swap_chain_images_len * sizeof(VkDeviceMemory));
+    }
+    if (!demo->overlay_image_views) {
+        demo->overlay_image_views =
+            malloc(demo->swap_chain_images_len * sizeof(VkImageView));
+    }
+
+    for (i = 0; i < demo->swap_chain_images_len; i++) {
+        result = vkCreateImage(demo->device, &image_info, NULL,
+                               &demo->overlay_images[i]);
+
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "vkCreateImage failed for index %lu: %d\n",
+                    (unsigned long)i, result);
+            return false;
+        }
+
+        vkGetImageMemoryRequirements(demo->device, demo->overlay_images[i],
+                                     &mem_requirements);
+
+        alloc_info.allocationSize = mem_requirements.size;
+
+        vkGetPhysicalDeviceMemoryProperties(demo->physical_device,
+                                            &mem_properties);
+        found = 0;
+        for (j = 0; j < mem_properties.memoryTypeCount; j++) {
+            if ((mem_requirements.memoryTypeBits & (1 << j)) &&
+                (mem_properties.memoryTypes[j].propertyFlags &
+                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ==
+                    VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+                found = 1;
+                break;
+            }
+        }
+        if (!found) {
+            fprintf(stderr,
+                    "failed to find suitable memory type for index %lu!\n",
+                    (unsigned long)i);
+            return false;
+        }
+        alloc_info.memoryTypeIndex = j;
+        result = vkAllocateMemory(demo->device, &alloc_info, NULL,
+                                  &demo->overlay_image_memories[i]);
+        if (result != VK_SUCCESS) {
+            fprintf(stderr,
+                    "failed to allocate vulkan memory for index %lu: %d!\n",
+                    (unsigned long)i, result);
+            return false;
+        }
+        result = vkBindImageMemory(demo->device, demo->overlay_images[i],
+                                   demo->overlay_image_memories[i], 0);
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "Couldn't bind image memory for index %lu: %d\n",
+                    (unsigned long)i, result);
+            return false;
+        }
+
+        image_view_info.image = demo->overlay_images[i];
+        result = vkCreateImageView(demo->device, &image_view_info, NULL,
+                                   &demo->overlay_image_views[i]);
+
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "vkCreateImageView failed for index %lu: %d\n",
+                    (unsigned long)i, result);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool create_render_pass(struct vulkan_demo *demo) {
+    VkAttachmentDescription attachment;
+    VkAttachmentReference color_attachment_ref;
+    VkSubpassDescription subpass;
+    VkSubpassDependency dependency;
+    VkRenderPassCreateInfo render_pass_info;
+    VkResult result;
+
+    memset(&attachment, 0, sizeof(VkAttachmentDescription));
+    attachment.format = demo->swap_chain_image_format;
+    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+    attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+    attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+    attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+    memset(&color_attachment_ref, 0, sizeof(VkAttachmentReference));
+    color_attachment_ref.attachment = 0;
+    color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+    memset(&subpass, 0, sizeof(VkSubpassDescription));
+    subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+    subpass.colorAttachmentCount = 1;
+    subpass.pColorAttachments = &color_attachment_ref;
+
+    memset(&dependency, 0, sizeof(VkSubpassDependency));
+    dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+    dependency.dstSubpass = 0;
+    dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    dependency.srcAccessMask = 0;
+    dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+                               VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+    memset(&render_pass_info, 0, sizeof(VkRenderPassCreateInfo));
+    render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+    render_pass_info.attachmentCount = 1;
+    render_pass_info.pAttachments = &attachment;
+    render_pass_info.subpassCount = 1;
+    render_pass_info.pSubpasses = &subpass;
+    render_pass_info.dependencyCount = 1;
+    render_pass_info.pDependencies = &dependency;
+
+    result = vkCreateRenderPass(demo->device, &render_pass_info, NULL,
+                                &demo->render_pass);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateRenderPass failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+bool create_framebuffers(struct vulkan_demo *demo) {
+    uint32_t i;
+    VkResult result;
+    VkFramebufferCreateInfo framebuffer_info;
+
+    if (!demo->framebuffers) {
+        demo->framebuffers =
+            malloc(demo->swap_chain_images_len * sizeof(VkFramebuffer));
+    }
+
+    memset(&framebuffer_info, 0, sizeof(VkFramebufferCreateInfo));
+    framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+    framebuffer_info.renderPass = demo->render_pass;
+    framebuffer_info.attachmentCount = 1;
+    framebuffer_info.width = demo->swap_chain_image_extent.width;
+    framebuffer_info.height = demo->swap_chain_image_extent.height;
+    framebuffer_info.layers = 1;
+
+    for (i = 0; i < demo->swap_chain_images_len; i++) {
+        framebuffer_info.pAttachments = &demo->swap_chain_image_views[i];
+
+        result = vkCreateFramebuffer(demo->device, &framebuffer_info, NULL,
+                                     &demo->framebuffers[i]);
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "vkCreateFramebuffer failed from index %lu: %d\n",
+                    (unsigned long)i, result);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool create_descriptor_set_layout(struct vulkan_demo *demo) {
+    VkDescriptorSetLayoutBinding overlay_layout_binding;
+    VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_nfo;
+    VkResult result;
+
+    memset(&overlay_layout_binding, 0, sizeof(VkDescriptorSetLayoutBinding));
+    overlay_layout_binding.binding = 0;
+    overlay_layout_binding.descriptorCount = 1;
+    overlay_layout_binding.descriptorType =
+        VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    overlay_layout_binding.pImmutableSamplers = NULL;
+    overlay_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+
+    memset(&descriptor_set_layout_create_nfo, 0,
+           sizeof(VkDescriptorSetLayoutCreateInfo));
+    descriptor_set_layout_create_nfo.sType =
+        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+    descriptor_set_layout_create_nfo.bindingCount = 1;
+    descriptor_set_layout_create_nfo.pBindings = &overlay_layout_binding;
+
+    result = vkCreateDescriptorSetLayout(demo->device,
+                                         &descriptor_set_layout_create_nfo,
+                                         NULL, &demo->descriptor_set_layout);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateDescriptorSetLayout failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+bool create_descriptor_pool(struct vulkan_demo *demo) {
+    VkDescriptorPoolSize pool_size;
+    VkDescriptorPoolCreateInfo pool_info;
+    VkResult result;
+
+    memset(&pool_size, 0, sizeof(VkDescriptorPoolSize));
+    pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    pool_size.descriptorCount = demo->swap_chain_images_len;
+
+    memset(&pool_info, 0, sizeof(VkDescriptorPoolCreateInfo));
+    pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+    pool_info.poolSizeCount = 1;
+    pool_info.pPoolSizes = &pool_size;
+    pool_info.maxSets = demo->swap_chain_images_len;
+    result = vkCreateDescriptorPool(demo->device, &pool_info, NULL,
+                                    &demo->descriptor_pool);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateDescriptorPool failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+void update_descriptor_sets(struct vulkan_demo *demo) {
+    uint32_t i;
+    VkDescriptorImageInfo descriptor_image_info;
+    VkWriteDescriptorSet descriptor_write;
+
+    memset(&descriptor_image_info, 0, sizeof(VkDescriptorImageInfo));
+    descriptor_image_info.imageLayout =
+        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+    descriptor_image_info.sampler = demo->sampler;
+
+    memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet));
+    descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    descriptor_write.dstBinding = 0;
+    descriptor_write.dstArrayElement = 0;
+    descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    descriptor_write.descriptorCount = 1;
+    descriptor_write.pImageInfo = &descriptor_image_info;
+
+    for (i = 0; i < demo->swap_chain_images_len; i++) {
+        descriptor_write.dstSet = demo->descriptor_sets[i];
+        descriptor_image_info.imageView = demo->overlay_image_views[i];
+
+        vkUpdateDescriptorSets(demo->device, 1, &descriptor_write, 0, NULL);
+    }
+}
+
+bool create_descriptor_sets(struct vulkan_demo *demo) {
+    bool ret = false;
+    VkDescriptorSetLayout *descriptor_set_layouts;
+    VkDescriptorSetAllocateInfo alloc_info;
+    uint32_t i;
+    VkResult result;
+
+    demo->descriptor_sets =
+        malloc(demo->swap_chain_images_len * sizeof(VkDescriptorSet));
+    descriptor_set_layouts =
+        malloc(demo->swap_chain_images_len * sizeof(VkDescriptorSetLayout));
+
+    for (i = 0; i < demo->swap_chain_images_len; i++) {
+        descriptor_set_layouts[i] = demo->descriptor_set_layout;
+    }
+
+    memset(&alloc_info, 0, sizeof(VkDescriptorSetAllocateInfo));
+    alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    alloc_info.descriptorPool = demo->descriptor_pool;
+    alloc_info.descriptorSetCount = demo->swap_chain_images_len;
+    alloc_info.pSetLayouts = descriptor_set_layouts;
+    result = vkAllocateDescriptorSets(demo->device, &alloc_info,
+                                      demo->descriptor_sets);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkAllocateDescriptorSets failed: %d\n", result);
+        goto cleanup;
+    }
+
+    update_descriptor_sets(demo);
+
+    ret = true;
+cleanup:
+    free(descriptor_set_layouts);
+
+    return ret;
+}
+
+bool create_shader_module(VkDevice device, char *shader_buffer,
+                          size_t shader_buffer_len,
+                          VkShaderModule *shader_module) {
+    VkShaderModuleCreateInfo create_info;
+    VkResult result;
+
+    memset(&create_info, 0, sizeof(VkShaderModuleCreateInfo));
+    create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+    create_info.codeSize = shader_buffer_len;
+    create_info.pCode = (const uint32_t *)shader_buffer;
+
+    result = vkCreateShaderModule(device, &create_info, NULL, shader_module);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateShaderModule failed: %d\n", result);
+        return false;
+    }
+
+    return true;
+}
+
+bool create_graphics_pipeline(struct vulkan_demo *demo) {
+    bool ret = false;
+    char *vert_shader_code = NULL;
+    char *frag_shader_code = NULL;
+    VkShaderModule vert_shader_module;
+    VkShaderModule frag_shader_module;
+    FILE *fp;
+    size_t file_len;
+    VkPipelineShaderStageCreateInfo vert_shader_stage_info;
+    VkPipelineShaderStageCreateInfo frag_shader_stage_info;
+    VkPipelineShaderStageCreateInfo shader_stages[2];
+    VkPipelineVertexInputStateCreateInfo vertex_input_info;
+    VkPipelineInputAssemblyStateCreateInfo input_assembly;
+    VkViewport viewport;
+    VkRect2D scissor;
+    VkPipelineViewportStateCreateInfo viewport_state;
+    VkPipelineRasterizationStateCreateInfo rasterizer;
+    VkPipelineMultisampleStateCreateInfo multisampling;
+    VkPipelineColorBlendAttachmentState color_blend_attachment;
+    VkPipelineColorBlendStateCreateInfo color_blending;
+    VkPipelineLayoutCreateInfo pipeline_layout_info;
+    VkResult result;
+    VkGraphicsPipelineCreateInfo pipeline_info;
+
+    fp = fopen("shaders/demo.vert.spv", "r");
+    if (!fp) {
+        fprintf(stderr, "Couldn't open shaders/demo.vert.spv\n");
+        return false;
+    }
+    fseek(fp, 0, SEEK_END);
+    file_len = ftell(fp);
+    vert_shader_code = malloc(file_len);
+    fseek(fp, 0, 0);
+    fread(vert_shader_code, 1, file_len, fp);
+    fclose(fp);
+
+    if (!create_shader_module(demo->device, vert_shader_code, file_len,
+                              &vert_shader_module)) {
+        goto cleanup;
+    }
+
+    fp = fopen("shaders/demo.frag.spv", "r");
+    if (!fp) {
+        fprintf(stderr, "Couldn't open shaders/demo.frag.spv\n");
+        return false;
+    }
+    fseek(fp, 0, SEEK_END);
+    file_len = ftell(fp);
+    frag_shader_code = malloc(file_len);
+    fseek(fp, 0, 0);
+    fread(frag_shader_code, 1, file_len, fp);
+    fclose(fp);
+
+    if (!create_shader_module(demo->device, frag_shader_code, file_len,
+                              &frag_shader_module)) {
+        goto cleanup;
+    }
+
+    memset(&vert_shader_stage_info, 0, sizeof(VkPipelineShaderStageCreateInfo));
+    vert_shader_stage_info.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+    vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
+    vert_shader_stage_info.module = vert_shader_module;
+    vert_shader_stage_info.pName = "main";
+
+    memset(&frag_shader_stage_info, 0, sizeof(VkPipelineShaderStageCreateInfo));
+    frag_shader_stage_info.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+    frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+    frag_shader_stage_info.module = frag_shader_module;
+    frag_shader_stage_info.pName = "main";
+
+    shader_stages[0] = vert_shader_stage_info;
+    shader_stages[1] = frag_shader_stage_info;
+
+    memset(&vertex_input_info, 0, sizeof(VkPipelineVertexInputStateCreateInfo));
+    vertex_input_info.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+
+    memset(&input_assembly, 0, sizeof(VkPipelineInputAssemblyStateCreateInfo));
+    input_assembly.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+    input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+    input_assembly.primitiveRestartEnable = VK_FALSE;
+
+    memset(&viewport, 0, sizeof(VkViewport));
+    viewport.x = 0.0f;
+    viewport.y = 0.0f;
+    viewport.width = (float)demo->swap_chain_image_extent.width;
+    viewport.height = (float)demo->swap_chain_image_extent.height;
+    viewport.minDepth = 0.0f;
+    viewport.maxDepth = 1.0f;
+
+    memset(&scissor, 0, sizeof(VkRect2D));
+    scissor.extent.width = demo->swap_chain_image_extent.width;
+    scissor.extent.height = demo->swap_chain_image_extent.height;
+
+    memset(&viewport_state, 0, sizeof(VkPipelineViewportStateCreateInfo));
+    viewport_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+    viewport_state.viewportCount = 1;
+    viewport_state.pViewports = &viewport;
+    viewport_state.scissorCount = 1;
+    viewport_state.pScissors = &scissor;
+
+    memset(&rasterizer, 0, sizeof(VkPipelineRasterizationStateCreateInfo));
+    rasterizer.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+    rasterizer.depthClampEnable = VK_FALSE;
+    rasterizer.rasterizerDiscardEnable = VK_FALSE;
+    rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
+    rasterizer.lineWidth = 1.0f;
+    rasterizer.cullMode = VK_CULL_MODE_FRONT_BIT;
+    rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+    rasterizer.depthBiasEnable = VK_FALSE;
+
+    memset(&multisampling, 0, sizeof(VkPipelineMultisampleStateCreateInfo));
+    multisampling.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+    multisampling.sampleShadingEnable = VK_FALSE;
+    multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+    memset(&color_blend_attachment, 0,
+           sizeof(VkPipelineColorBlendAttachmentState));
+    color_blend_attachment.colorWriteMask =
+        VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+        VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+    color_blend_attachment.blendEnable = VK_TRUE;
+    color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
+    color_blend_attachment.dstColorBlendFactor =
+        VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+    color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
+    color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+    color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+    color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
+
+    memset(&color_blending, 0, sizeof(VkPipelineColorBlendStateCreateInfo));
+    color_blending.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+    color_blending.logicOpEnable = VK_FALSE;
+    color_blending.logicOp = VK_LOGIC_OP_COPY;
+    color_blending.attachmentCount = 1;
+    color_blending.pAttachments = &color_blend_attachment;
+    color_blending.blendConstants[0] = 1.0f;
+    color_blending.blendConstants[1] = 1.0f;
+    color_blending.blendConstants[2] = 1.0f;
+    color_blending.blendConstants[3] = 1.0f;
+
+    memset(&pipeline_layout_info, 0, sizeof(VkPipelineLayoutCreateInfo));
+    pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+    pipeline_layout_info.setLayoutCount = 0;
+    pipeline_layout_info.pushConstantRangeCount = 0;
+    pipeline_layout_info.setLayoutCount = 1;
+    pipeline_layout_info.pSetLayouts = &demo->descriptor_set_layout;
+
+    result = vkCreatePipelineLayout(demo->device, &pipeline_layout_info, NULL,
+                                    &demo->pipeline_layout);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreatePipelineLayout failed: %d\n", result);
+        goto cleanup;
+    }
+
+    memset(&pipeline_info, 0, sizeof(VkGraphicsPipelineCreateInfo));
+    pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+    pipeline_info.stageCount = 2;
+    pipeline_info.pStages = shader_stages;
+    pipeline_info.pVertexInputState = &vertex_input_info;
+    pipeline_info.pInputAssemblyState = &input_assembly;
+    pipeline_info.pViewportState = &viewport_state;
+    pipeline_info.pRasterizationState = &rasterizer;
+    pipeline_info.pMultisampleState = &multisampling;
+    pipeline_info.pColorBlendState = &color_blending;
+    pipeline_info.layout = demo->pipeline_layout;
+    pipeline_info.renderPass = demo->render_pass;
+    pipeline_info.basePipelineHandle = NULL;
+
+    result = vkCreateGraphicsPipelines(demo->device, NULL, 1, &pipeline_info,
+                                       NULL, &demo->pipeline);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateGraphicsPipelines failed: %d\n", result);
+        goto cleanup;
+    }
+    ret = true;
+cleanup:
+    if (frag_shader_module) {
+        vkDestroyShaderModule(demo->device, frag_shader_module, NULL);
+    }
+    if (frag_shader_code) {
+        free(frag_shader_code);
+    }
+    if (vert_shader_module) {
+        vkDestroyShaderModule(demo->device, vert_shader_module, NULL);
+    }
+    if (vert_shader_code) {
+        free(vert_shader_code);
+    }
+
+    return ret;
+}
+
+bool create_command_pool(struct vulkan_demo *demo) {
+    VkCommandPoolCreateInfo pool_info;
+    VkResult result;
+
+    memset(&pool_info, 0, sizeof(VkCommandPoolCreateInfo));
+    pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+    pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+    pool_info.queueFamilyIndex = demo->indices.graphics;
+
+    result = vkCreateCommandPool(demo->device, &pool_info, NULL,
+                                 &demo->command_pool);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateCommandPool failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+bool create_command_buffers(struct vulkan_demo *demo) {
+    VkCommandBufferAllocateInfo alloc_info;
+    VkResult result;
+
+    demo->command_buffers =
+        malloc(demo->swap_chain_images_len * sizeof(VkCommandBuffer));
+
+    memset(&alloc_info, 0, sizeof(VkCommandBufferAllocateInfo));
+    alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    alloc_info.commandPool = demo->command_pool;
+    alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    alloc_info.commandBufferCount = demo->swap_chain_images_len;
+
+    result = vkAllocateCommandBuffers(demo->device, &alloc_info,
+                                      demo->command_buffers);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkAllocateCommandBuffers failed: %d\n", result);
+        return false;
+    }
+
+    return true;
+}
+
+bool create_semaphores(struct vulkan_demo *demo) {
+    VkSemaphoreCreateInfo semaphore_info;
+    VkResult result;
+
+    memset(&semaphore_info, 0, sizeof(VkSemaphoreCreateInfo));
+    semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+    result = vkCreateSemaphore(demo->device, &semaphore_info, NULL,
+                               &demo->image_available);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateSemaphore failed: %d\n", result);
+        return false;
+    }
+    result = vkCreateSemaphore(demo->device, &semaphore_info, NULL,
+                               &demo->render_finished);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateSemaphore failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+bool create_fence(struct vulkan_demo *demo) {
+    VkResult result;
+    VkFenceCreateInfo fence_create_info;
+
+    memset(&fence_create_info, 0, sizeof(VkFenceCreateInfo));
+    fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+    fence_create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+    result = vkCreateFence(demo->device, &fence_create_info, NULL,
+                           &demo->render_fence);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateFence failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+bool create_swap_chain_related_resources(struct vulkan_demo *demo) {
+    if (!create_swap_chain(demo)) {
+        return false;
+    }
+    if (!create_swap_chain_image_views(demo)) {
+        return false;
+    }
+    if (!create_overlay_images(demo)) {
+        return false;
+    }
+    if (!create_render_pass(demo)) {
+        return false;
+    }
+    if (!create_framebuffers(demo)) {
+        return false;
+    }
+    if (!create_graphics_pipeline(demo)) {
+        return false;
+    }
+    return true;
+}
+
+bool destroy_swap_chain_related_resources(struct vulkan_demo *demo) {
+    uint32_t i;
+    VkResult result;
+
+    result = vkQueueWaitIdle(demo->graphics_queue);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkQueueWaitIdle failed: %d\n", result);
+        return false;
+    }
+
+    for (i = 0; i < demo->swap_chain_images_len; i++) {
+        vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL);
+        vkDestroyImageView(demo->device, demo->overlay_image_views[i], NULL);
+        vkDestroyImage(demo->device, demo->overlay_images[i], NULL);
+        vkFreeMemory(demo->device, demo->overlay_image_memories[i], NULL);
+        vkDestroyImageView(demo->device, demo->swap_chain_image_views[i], NULL);
+    }
+    vkDestroySwapchainKHR(demo->device, demo->swap_chain, NULL);
+    vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
+    vkDestroyPipeline(demo->device, demo->pipeline, NULL);
+    vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
+    return true;
+}
+
+bool create_demo_texture(struct vulkan_demo *demo) {
+    VkResult result;
+    VkMemoryRequirements mem_requirements;
+    VkPhysicalDeviceMemoryProperties mem_properties;
+    int found;
+    uint32_t i;
+    VkImageCreateInfo image_info;
+    VkMemoryAllocateInfo alloc_info;
+    VkImageViewCreateInfo image_view_info;
+    VkBufferCreateInfo buffer_info;
+    struct {
+        VkDeviceMemory memory;
+        VkBuffer buffer;
+    } staging_buffer;
+    void *data;
+    VkCommandBuffer command_buffer;
+    VkCommandBufferBeginInfo begin_info;
+    VkImageMemoryBarrier image_transfer_dst_memory_barrier;
+    VkBufferImageCopy buffer_copy_region;
+    VkImageMemoryBarrier image_shader_memory_barrier;
+    VkFence fence;
+    VkFenceCreateInfo fence_create;
+    VkSubmitInfo submit_info;
+
+    memset(&image_info, 0, sizeof(VkImageCreateInfo));
+    image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    image_info.imageType = VK_IMAGE_TYPE_2D;
+    image_info.extent.width = 2;
+    image_info.extent.height = 2;
+    image_info.extent.depth = 1;
+    image_info.mipLevels = 1;
+    image_info.arrayLayers = 1;
+    image_info.format = VK_FORMAT_R8_UNORM;
+    image_info.tiling = VK_IMAGE_TILING_LINEAR;
+    image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    image_info.usage =
+        VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+    image_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+    memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo));
+    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+
+    memset(&image_view_info, 0, sizeof(VkImageViewCreateInfo));
+    image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    image_view_info.format = VK_FORMAT_R8_UNORM;
+    image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    image_view_info.subresourceRange.baseMipLevel = 0;
+    image_view_info.subresourceRange.levelCount = 1;
+    image_view_info.subresourceRange.baseArrayLayer = 0;
+    image_view_info.subresourceRange.layerCount = 1;
+
+    result = vkCreateImage(demo->device, &image_info, NULL,
+                           &demo->demo_texture_image);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateImage failed: %d\n", result);
+        return false;
+    }
+
+    vkGetImageMemoryRequirements(demo->device, demo->demo_texture_image,
+                                 &mem_requirements);
+
+    alloc_info.allocationSize = mem_requirements.size;
+
+    vkGetPhysicalDeviceMemoryProperties(demo->physical_device, &mem_properties);
+    found = 0;
+    for (i = 0; i < mem_properties.memoryTypeCount; i++) {
+        if ((mem_requirements.memoryTypeBits & (1 << i)) &&
+            (mem_properties.memoryTypes[i].propertyFlags &
+             VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ==
+                VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+            found = 1;
+            break;
+        }
+    }
+    if (!found) {
+        fprintf(stderr, "failed to find suitable memory for demo texture!\n");
+        return false;
+    }
+    alloc_info.memoryTypeIndex = i;
+    result = vkAllocateMemory(demo->device, &alloc_info, NULL,
+                              &demo->demo_texture_memory);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr,
+                "failed to allocate vulkan memory for demo texture: %d!\n",
+                result);
+        return false;
+    }
+    result = vkBindImageMemory(demo->device, demo->demo_texture_image,
+                               demo->demo_texture_memory, 0);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "Couldn't bind image memory for demo texture: %d\n",
+                result);
+        return false;
+    }
+
+    image_view_info.image = demo->demo_texture_image;
+    result = vkCreateImageView(demo->device, &image_view_info, NULL,
+                               &demo->demo_texture_image_view);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateImageView failed for demo texture: %d\n",
+                result);
+        return false;
+    }
+
+    memset(&buffer_info, 0, sizeof(VkBufferCreateInfo));
+    buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+    buffer_info.size = alloc_info.allocationSize;
+    buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+    buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+    result = vkCreateBuffer(demo->device, &buffer_info, NULL,
+                            &staging_buffer.buffer);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateBuffer failed for demo texture: %d\n", result);
+        return false;
+    }
+    vkGetBufferMemoryRequirements(demo->device, staging_buffer.buffer,
+                                  &mem_requirements);
+
+    alloc_info.allocationSize = mem_requirements.size;
+    found = 0;
+    for (i = 0; i < mem_properties.memoryTypeCount; i++) {
+        if ((mem_requirements.memoryTypeBits & (1 << i)) &&
+            (mem_properties.memoryTypes[i].propertyFlags &
+             (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+              VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
+                (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+                 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
+            found = 1;
+            break;
+        }
+    }
+    if (!found) {
+        fprintf(stderr, "failed to find suitable staging buffer memory for "
+                        "demo texture!\n");
+        return false;
+    }
+    alloc_info.memoryTypeIndex = i;
+    result = vkAllocateMemory(demo->device, &alloc_info, NULL,
+                              &staging_buffer.memory);
+    if (!found) {
+        fprintf(stderr, "vkAllocateMemory failed for demo texture: %d\n",
+                result);
+        return false;
+    }
+    result = vkBindBufferMemory(demo->device, staging_buffer.buffer,
+                                staging_buffer.memory, 0);
+    if (!found) {
+        fprintf(stderr, "vkBindBufferMemory failed for demo texture: %d\n",
+                result);
+        return false;
+    }
+
+    result = vkMapMemory(demo->device, staging_buffer.memory, 0,
+                         sizeof(uint32_t), 0, &data);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkMapMemory failed for demo texture: %d\n", result);
+        return false;
+    }
+    *((uint32_t *)data) = 0x00FFFF00;
+    vkUnmapMemory(demo->device, staging_buffer.memory);
+
+    memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo));
+    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+    command_buffer = demo->command_buffers[0];
+    result = vkBeginCommandBuffer(command_buffer, &begin_info);
+
+    memset(&image_transfer_dst_memory_barrier, 0, sizeof(VkImageMemoryBarrier));
+    image_transfer_dst_memory_barrier.sType =
+        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    image_transfer_dst_memory_barrier.image = demo->demo_texture_image;
+    image_transfer_dst_memory_barrier.srcQueueFamilyIndex =
+        VK_QUEUE_FAMILY_IGNORED;
+    image_transfer_dst_memory_barrier.dstQueueFamilyIndex =
+        VK_QUEUE_FAMILY_IGNORED;
+    image_transfer_dst_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    image_transfer_dst_memory_barrier.newLayout =
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+    image_transfer_dst_memory_barrier.subresourceRange.aspectMask =
+        VK_IMAGE_ASPECT_COLOR_BIT;
+    image_transfer_dst_memory_barrier.subresourceRange.levelCount = 1;
+    image_transfer_dst_memory_barrier.subresourceRange.layerCount = 1;
+    image_transfer_dst_memory_barrier.dstAccessMask =
+        VK_ACCESS_TRANSFER_WRITE_BIT;
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+                         VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
+                         &image_transfer_dst_memory_barrier);
+
+    memset(&buffer_copy_region, 0, sizeof(VkBufferImageCopy));
+    buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    buffer_copy_region.imageSubresource.layerCount = 1;
+    buffer_copy_region.imageExtent.width = 2;
+    buffer_copy_region.imageExtent.height = 2;
+    buffer_copy_region.imageExtent.depth = 1;
+
+    vkCmdCopyBufferToImage(
+        command_buffer, staging_buffer.buffer, demo->demo_texture_image,
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_copy_region);
+
+    memset(&image_shader_memory_barrier, 0, sizeof(VkImageMemoryBarrier));
+    image_shader_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    image_shader_memory_barrier.image = demo->demo_texture_image;
+    image_shader_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_shader_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_shader_memory_barrier.oldLayout =
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+    image_shader_memory_barrier.newLayout =
+        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+    image_shader_memory_barrier.subresourceRange.aspectMask =
+        VK_IMAGE_ASPECT_COLOR_BIT;
+    image_shader_memory_barrier.subresourceRange.levelCount = 1;
+    image_shader_memory_barrier.subresourceRange.layerCount = 1;
+    image_shader_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+    image_shader_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
+                         VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0,
+                         NULL, 1, &image_shader_memory_barrier);
+
+    result = vkEndCommandBuffer(command_buffer);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEndCommandBuffer failed for demo texture: %d\n",
+                result);
+        return false;
+    }
+
+    memset(&fence_create, 0, sizeof(VkFenceCreateInfo));
+    fence_create.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+    result = vkCreateFence(demo->device, &fence_create, NULL, &fence);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkCreateFence failed for demo texture: %d\n", result);
+        return false;
+    }
+
+    memset(&submit_info, 0, sizeof(VkSubmitInfo));
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &command_buffer;
+
+    result = vkQueueSubmit(demo->graphics_queue, 1, &submit_info, fence);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkQueueSubmit failed for demo texture: %d\n", result);
+        return false;
+    }
+    result = vkWaitForFences(demo->device, 1, &fence, VK_TRUE, UINT64_MAX);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkWaitForFences failed for demo texture: %d\n",
+                result);
+        return false;
+    }
+
+    vkDestroyBuffer(demo->device, staging_buffer.buffer, NULL);
+    vkFreeMemory(demo->device, staging_buffer.memory, NULL);
+    vkDestroyFence(demo->device, fence, NULL);
+
+    return true;
+}
+
+bool create_vulkan_demo(struct vulkan_demo *demo) {
+    if (!create_instance(demo)) {
+        return false;
+    }
+    if (!create_surface(demo)) {
+        return false;
+    }
+    if (!create_physical_device(demo)) {
+        return false;
+    }
+    if (!create_logical_device(demo)) {
+        return false;
+    }
+    if (!create_sampler(demo)) {
+        return false;
+    }
+    if (!create_descriptor_set_layout(demo)) {
+        return false;
+    }
+    if (!create_swap_chain_related_resources(demo)) {
+        return false;
+    }
+    if (!create_descriptor_pool(demo)) {
+        return false;
+    }
+    if (!create_descriptor_sets(demo)) {
+        return false;
+    }
+    if (!create_command_pool(demo)) {
+        return false;
+    }
+    if (!create_command_buffers(demo)) {
+        return false;
+    }
+    if (!create_semaphores(demo)) {
+        return false;
+    }
+    if (!create_fence(demo)) {
+        return false;
+    }
+    if (!create_demo_texture(demo)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool recreate_swap_chain(struct vulkan_demo *demo) {
+    printf("recreating swapchain\n");
+    if (!destroy_swap_chain_related_resources(demo)) {
+        return false;
+    }
+    if (!create_swap_chain_related_resources(demo)) {
+        return false;
+    }
+    update_descriptor_sets(demo);
+    nk_glfw3_resize(demo->swap_chain_image_extent.width,
+                    demo->swap_chain_image_extent.height);
+    return true;
+}
+
+bool render(struct vulkan_demo *demo, struct nk_colorf *bg,
+            VkSemaphore wait_semaphore, uint32_t image_index) {
+    VkCommandBufferBeginInfo command_buffer_begin_info;
+    VkCommandBuffer command_buffer;
+    VkRenderPassBeginInfo render_pass_info;
+    VkSubmitInfo submit_info;
+    VkPipelineStageFlags wait_stage =
+        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    VkResult result;
+    VkPresentInfoKHR present_info;
+    VkClearValue clear_color;
+
+    memcpy(&clear_color.color, bg, sizeof(VkClearColorValue));
+
+    memset(&command_buffer_begin_info, 0, sizeof(VkCommandBufferBeginInfo));
+    command_buffer_begin_info.sType =
+        VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+    command_buffer = demo->command_buffers[image_index];
+    result = vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkBeginCommandBuffer failed: %d\n", result);
+        return false;
+    }
+
+    memset(&render_pass_info, 0, sizeof(VkRenderPassBeginInfo));
+    render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+    render_pass_info.renderPass = demo->render_pass;
+    render_pass_info.framebuffer = demo->framebuffers[image_index];
+    render_pass_info.renderArea.offset.x = 0;
+    render_pass_info.renderArea.offset.y = 0;
+    render_pass_info.renderArea.extent = demo->swap_chain_image_extent;
+    render_pass_info.clearValueCount = 1;
+    render_pass_info.pClearValues = &clear_color;
+
+    vkCmdBeginRenderPass(command_buffer, &render_pass_info,
+                         VK_SUBPASS_CONTENTS_INLINE);
+
+    vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                      demo->pipeline);
+    vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                            demo->pipeline_layout, 0, 1,
+                            &demo->descriptor_sets[image_index], 0, NULL);
+    vkCmdDraw(command_buffer, 3, 1, 0, 0);
+
+    vkCmdEndRenderPass(command_buffer);
+
+    result = vkEndCommandBuffer(command_buffer);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkEndCommandBuffer failed: %d\n", result);
+        return false;
+    }
+
+    memset(&submit_info, 0, sizeof(VkSubmitInfo));
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.waitSemaphoreCount = 1;
+    submit_info.pWaitSemaphores = &wait_semaphore;
+    submit_info.pWaitDstStageMask = &wait_stage;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &demo->command_buffers[image_index];
+    submit_info.signalSemaphoreCount = 1;
+    submit_info.pSignalSemaphores = &demo->render_finished;
+
+    result = vkQueueSubmit(demo->graphics_queue, 1, &submit_info,
+                           demo->render_fence);
+
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkQueueSubmit failed: %d\n", result);
+        return false;
+    }
+
+    memset(&present_info, 0, sizeof(VkPresentInfoKHR));
+    present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+    present_info.waitSemaphoreCount = 1;
+    present_info.pWaitSemaphores = &demo->render_finished;
+    present_info.swapchainCount = 1;
+    present_info.pSwapchains = &demo->swap_chain;
+    present_info.pImageIndices = &image_index;
+
+    result = vkQueuePresentKHR(demo->present_queue, &present_info);
+
+    if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
+        recreate_swap_chain(demo);
+    } else if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkQueuePresentKHR failed: %d\n", result);
+        return false;
+    }
+    return true;
+}
+
+VkResult
+destroy_debug_utils_messenger_ext(VkInstance instance,
+                                  VkDebugUtilsMessengerEXT debugMessenger,
+                                  const VkAllocationCallbacks *pAllocator) {
+    PFN_vkDestroyDebugUtilsMessengerEXT func =
+        (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
+            instance, "vkDestroyDebugUtilsMessengerEXT");
+    if (func != NULL) {
+        func(instance, debugMessenger, pAllocator);
+        return VK_SUCCESS;
+    } else {
+        return VK_ERROR_EXTENSION_NOT_PRESENT;
+    }
+}
+
+bool cleanup(struct vulkan_demo *demo) {
+    VkResult result;
+
+    printf("cleaning up\n");
+    result = vkDeviceWaitIdle(demo->device);
+    if (result != VK_SUCCESS) {
+        fprintf(stderr, "vkDeviceWaitIdle failed: %d\n", result);
+        return false;
+    }
+
+    destroy_swap_chain_related_resources(demo);
+
+    vkFreeCommandBuffers(demo->device, demo->command_pool,
+                         demo->swap_chain_images_len, demo->command_buffers);
+    vkDestroyCommandPool(demo->device, demo->command_pool, NULL);
+    vkDestroySampler(demo->device, demo->sampler, NULL);
+    vkDestroySemaphore(demo->device, demo->render_finished, NULL);
+    vkDestroySemaphore(demo->device, demo->image_available, NULL);
+    vkDestroyFence(demo->device, demo->render_fence, NULL);
+
+    vkDestroyImage(demo->device, demo->demo_texture_image, NULL);
+    vkDestroyImageView(demo->device, demo->demo_texture_image_view, NULL);
+    vkFreeMemory(demo->device, demo->demo_texture_memory, NULL);
+
+    vkDestroyDescriptorSetLayout(demo->device, demo->descriptor_set_layout,
+                                 NULL);
+    vkDestroyDescriptorPool(demo->device, demo->descriptor_pool, NULL);
+
+    vkDestroyDevice(demo->device, NULL);
+    vkDestroySurfaceKHR(demo->instance, demo->surface, NULL);
+
+    if (demo->debug_messenger) {
+        result = destroy_debug_utils_messenger_ext(demo->instance,
+                                                   demo->debug_messenger, NULL);
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "Couldn't destroy debug messenger: %d\n", result);
+            return false;
+        }
+    }
+    vkDestroyInstance(demo->instance, NULL);
+    if (demo->swap_chain_images) {
+        free(demo->swap_chain_images);
+    }
+    if (demo->swap_chain_image_views) {
+        free(demo->swap_chain_image_views);
+    }
+
+    if (demo->overlay_images) {
+        free(demo->overlay_images);
+    }
+    if (demo->overlay_image_views) {
+        free(demo->overlay_image_views);
+    }
+    if (demo->overlay_image_memories) {
+        free(demo->overlay_image_memories);
+    }
+
+    if (demo->descriptor_sets) {
+        free(demo->descriptor_sets);
+    }
+    if (demo->framebuffers) {
+        free(demo->framebuffers);
+    }
+    if (demo->command_buffers) {
+        free(demo->command_buffers);
+    }
+
+    return true;
+}
+
+int main(void) {
+    struct vulkan_demo demo;
+    struct nk_context *ctx;
+    struct nk_colorf bg;
+    struct nk_image img;
+    uint32_t image_index;
+    VkResult result;
+    VkSemaphore nk_semaphore;
+
+    glfwSetErrorCallback(glfw_error_callback);
+    if (!glfwInit()) {
+        fprintf(stderr, "[GFLW] failed to init!\n");
+        exit(1);
+    }
+    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+    memset(&demo, 0, sizeof(struct vulkan_demo));
+    demo.win =
+        glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL);
+
+    if (!create_vulkan_demo(&demo)) {
+        fprintf(stderr, "failed to create vulkan demo!\n");
+        exit(1);
+    }
+    ctx = nk_glfw3_init(
+        demo.win, demo.device, demo.physical_device, demo.indices.graphics,
+        demo.overlay_image_views, demo.swap_chain_images_len,
+        demo.swap_chain_image_format, NK_GLFW3_INSTALL_CALLBACKS,
+        MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER);
+    /* Load Fonts: if none of these are loaded a default font will be used  */
+    /* Load Cursor: if you uncomment cursor loading please hide the cursor */
+    {
+        struct nk_font_atlas *atlas;
+        nk_glfw3_font_stash_begin(&atlas);
+        /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas,
+         * "../../../extra_font/DroidSans.ttf", 14, 0);*/
+        /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas,
+         * "../../../extra_font/Roboto-Regular.ttf", 14, 0);*/
+        /*struct nk_font *future = nk_font_atlas_add_from_file(atlas,
+         * "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/
+        /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas,
+         * "../../../extra_font/ProggyClean.ttf", 12, 0);*/
+        /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas,
+         * "../../../extra_font/ProggyTiny.ttf", 10, 0);*/
+        /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas,
+         * "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/
+        nk_glfw3_font_stash_end(demo.graphics_queue);
+        /*nk_style_load_all_cursors(ctx, atlas->cursors);*/
+    /*nk_style_set_font(ctx, &droid->handle);*/}
+
+#ifdef INCLUDE_STYLE
+    /* ease regression testing during Nuklear release process; not needed for
+     * anything else */
+#ifdef STYLE_WHITE
+    set_style(ctx, THEME_WHITE);
+#elif defined(STYLE_RED)
+    set_style(ctx, THEME_RED);
+#elif defined(STYLE_BLUE)
+    set_style(ctx, THEME_BLUE);
+#elif defined(STYLE_DARK)
+    set_style(ctx, THEME_DARK);
+#endif
+#endif
+
+    img = nk_image_ptr(demo.demo_texture_image_view);
+    bg.r = 0.10f, bg.g = 0.18f, bg.b = 0.24f, bg.a = 1.0f;
+    while (!glfwWindowShouldClose(demo.win)) {
+        /* Input */
+        glfwPollEvents();
+        nk_glfw3_new_frame();
+
+        /* GUI */
+        if (nk_begin(ctx, "Demo", nk_rect(50, 50, 230, 250),
+                     NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE |
+                         NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE)) {
+            enum { EASY, HARD };
+            static int op = EASY;
+            static int property = 20;
+            nk_layout_row_static(ctx, 30, 80, 1);
+            if (nk_button_label(ctx, "button"))
+                fprintf(stdout, "button pressed\n");
+
+            nk_layout_row_dynamic(ctx, 30, 2);
+            if (nk_option_label(ctx, "easy", op == EASY))
+                op = EASY;
+            if (nk_option_label(ctx, "hard", op == HARD))
+                op = HARD;
+
+            nk_layout_row_dynamic(ctx, 25, 1);
+            nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1);
+
+            nk_layout_row_dynamic(ctx, 20, 1);
+            nk_label(ctx, "background:", NK_TEXT_LEFT);
+            nk_layout_row_dynamic(ctx, 25, 1);
+            if (nk_combo_begin_color(ctx, nk_rgb_cf(bg),
+                                     nk_vec2(nk_widget_width(ctx), 400))) {
+                nk_layout_row_dynamic(ctx, 120, 1);
+                bg = nk_color_picker(ctx, bg, NK_RGBA);
+                nk_layout_row_dynamic(ctx, 25, 1);
+                bg.r = nk_propertyf(ctx, "#R:", 0, bg.r, 1.0f, 0.01f, 0.005f);
+                bg.g = nk_propertyf(ctx, "#G:", 0, bg.g, 1.0f, 0.01f, 0.005f);
+                bg.b = nk_propertyf(ctx, "#B:", 0, bg.b, 1.0f, 0.01f, 0.005f);
+                bg.a = nk_propertyf(ctx, "#A:", 0, bg.a, 1.0f, 0.01f, 0.005f);
+                nk_combo_end(ctx);
+            }
+        }
+        nk_end(ctx);
+
+        /* Bindless Texture */
+        if (nk_begin(ctx, "Texture", nk_rect(500, 300, 200, 200),
+                     NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE |
+                         NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE)) {
+            struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
+            struct nk_rect total_space = nk_window_get_content_region(ctx);
+            nk_draw_image(canvas, total_space, &img, nk_white);
+        }
+        nk_end(ctx);
+
+        /* -------------- EXAMPLES ---------------- */
+#ifdef INCLUDE_CALCULATOR
+        calculator(ctx);
+#endif
+#ifdef INCLUDE_CANVAS
+        canvas(ctx);
+#endif
+#ifdef INCLUDE_OVERVIEW
+        overview(ctx);
+#endif
+#ifdef INCLUDE_NODE_EDITOR
+        node_editor(ctx);
+#endif
+        /* ----------------------------------------- */
+
+        result = vkWaitForFences(demo.device, 1, &demo.render_fence, VK_TRUE,
+                                 UINT64_MAX);
+
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "vkWaitForFences failed: %d\n", result);
+            return false;
+        }
+
+        result = vkResetFences(demo.device, 1, &demo.render_fence);
+        if (result != VK_SUCCESS) {
+            fprintf(stderr, "vkResetFences failed: %d\n", result);
+            return false;
+        }
+
+        result =
+            vkAcquireNextImageKHR(demo.device, demo.swap_chain, UINT64_MAX,
+                                  demo.image_available, NULL, &image_index);
+
+        if (result == VK_ERROR_OUT_OF_DATE_KHR) {
+            continue;
+        }
+        if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
+            fprintf(stderr, "vkAcquireNextImageKHR failed: %d\n", result);
+            return false;
+        }
+
+        /* Draw */
+        nk_semaphore =
+            nk_glfw3_render(demo.graphics_queue, image_index,
+                            demo.image_available, NK_ANTI_ALIASING_ON);
+        if (!render(&demo, &bg, nk_semaphore, image_index)) {
+            fprintf(stderr, "render failed\n");
+            return false;
+        }
+    }
+    nk_glfw3_shutdown();
+    cleanup(&demo);
+    glfwTerminate();
+    return 0;
+}

+ 1649 - 0
demo/glfw_vulkan/nuklear_glfw_vulkan.h

@@ -0,0 +1,1649 @@
+/*
+ * Nuklear - 1.32.0 - public domain
+ * no warrenty implied; use at your own risk.
+ * authored from 2015-2016 by Micha Mettke
+ */
+/*
+ * ==============================================================
+ *
+ *                              API
+ *
+ * ===============================================================
+ */
+#ifndef NK_GLFW_VULKAN_H_
+#define NK_GLFW_VULKAN_H_
+
+unsigned char nuklearshaders_nuklear_vert_spv[] = {
+  0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00,
+  0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
+  0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
+  0x2a, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00,
+  0x04, 0x00, 0x09, 0x00, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73,
+  0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x64,
+  0x65, 0x72, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x00, 0x00,
+  0x04, 0x00, 0x0a, 0x00, 0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c,
+  0x45, 0x5f, 0x63, 0x70, 0x70, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f,
+  0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,
+  0x76, 0x65, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4c, 0x5f, 0x47,
+  0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64,
+  0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00,
+  0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e,
+  0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78,
+  0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74,
+  0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x42, 0x75, 0x66, 0x66, 0x65,
+  0x72, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x06, 0x00, 0x06, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x6f, 0x6a,
+  0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x75, 0x62, 0x6f, 0x00, 0x05, 0x00, 0x05, 0x00,
+  0x16, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
+  0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00,
+  0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00,
+  0x66, 0x72, 0x61, 0x67, 0x55, 0x76, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
+  0x43, 0x00, 0x00, 0x00, 0x75, 0x76, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x47, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+  0x16, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x47, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00,
+  0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+  0x42, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x47, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x15, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00,
+  0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x1e, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+  0x20, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+  0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+  0x17, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
+  0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x20, 0x00, 0x04, 0x00,
+  0x1e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x15, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+  0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x3b, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00,
+  0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+  0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+  0x3b, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x7f, 0x43, 0x2b, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x36, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
+  0x20, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x20, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00,
+  0x42, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
+  0x15, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00,
+  0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x12, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x1b, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+  0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+  0x19, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x1d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+  0x41, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
+  0x1f, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
+  0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+  0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+  0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x22, 0x00, 0x00, 0x00,
+  0x26, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x21, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00,
+  0x25, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x2d, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00,
+  0x3d, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+  0x2d, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x2f, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x32, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+  0x3d, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+  0x32, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x37, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
+  0x3d, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+  0x37, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x3c, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00,
+  0x3d, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00,
+  0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x40, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
+  0x3a, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
+  0x27, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+  0x3e, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+  0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
+};
+unsigned int nuklearshaders_nuklear_vert_spv_len = 1856;
+unsigned char nuklearshaders_nuklear_frag_spv[] = {
+  0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00,
+  0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
+  0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
+  0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00,
+  0x04, 0x00, 0x09, 0x00, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73,
+  0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x64,
+  0x65, 0x72, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x00, 0x00,
+  0x04, 0x00, 0x0a, 0x00, 0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c,
+  0x45, 0x5f, 0x63, 0x70, 0x70, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f,
+  0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,
+  0x76, 0x65, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4c, 0x5f, 0x47,
+  0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64,
+  0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00,
+  0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e,
+  0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x74, 0x65, 0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x72,
+  0x65, 0x6e, 0x74, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00,
+  0x05, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67,
+  0x55, 0x76, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00,
+  0x6f, 0x75, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67,
+  0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+  0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
+  0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+  0x15, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00,
+  0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+  0x3b, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+  0x3b, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x20, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00,
+  0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
+  0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
+  0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+  0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+  0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x1a, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+  0x3e, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+  0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
+};
+unsigned int nuklearshaders_nuklear_frag_spv_len = 860;
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#define GLFW_INCLUDE_VULKAN
+#include <GLFW/glfw3.h>
+
+enum nk_glfw_init_state { NK_GLFW3_DEFAULT = 0, NK_GLFW3_INSTALL_CALLBACKS };
+
+NK_API struct nk_context *
+nk_glfw3_init(GLFWwindow *win, VkDevice logical_device,
+              VkPhysicalDevice physical_device,
+              uint32_t graphics_queue_family_index, VkImageView *image_views,
+              uint32_t image_views_len, VkFormat color_format,
+              enum nk_glfw_init_state init_state,
+              VkDeviceSize max_vertex_buffer, VkDeviceSize max_element_buffer);
+NK_API void nk_glfw3_shutdown(void);
+NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas);
+NK_API void nk_glfw3_font_stash_end(VkQueue graphics_queue);
+NK_API void nk_glfw3_new_frame();
+NK_API VkSemaphore nk_glfw3_render(VkQueue graphics_queue,
+                                   uint32_t buffer_index,
+                                   VkSemaphore wait_semaphore,
+                                   enum nk_anti_aliasing AA);
+NK_API void nk_glfw3_resize(uint32_t framebuffer_width,
+                            uint32_t framebuffer_height);
+NK_API void nk_glfw3_device_destroy(void);
+NK_API void nk_glfw3_device_create(
+    VkDevice logical_device, VkPhysicalDevice physical_device,
+    uint32_t graphics_queue_family_index, VkImageView *image_views,
+    uint32_t image_views_len, VkFormat color_format,
+    VkDeviceSize max_vertex_buffer, VkDeviceSize max_element_buffer,
+    uint32_t framebuffer_width, uint32_t framebuffer_height);
+
+NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
+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);
+
+#endif
+/*
+ * ==============================================================
+ *
+ *                          IMPLEMENTATION
+ *
+ * ===============================================================
+ */
+#ifdef NK_GLFW_VULKAN_IMPLEMENTATION
+#undef NK_GLFW_VULKAN_IMPLEMENTATION
+#include <stdlib.h>
+
+#ifndef NK_GLFW_TEXT_MAX
+#define NK_GLFW_TEXT_MAX 256
+#endif
+#ifndef NK_GLFW_DOUBLE_CLICK_LO
+#define NK_GLFW_DOUBLE_CLICK_LO 0.02
+#endif
+#ifndef NK_GLFW_DOUBLE_CLICK_HI
+#define NK_GLFW_DOUBLE_CLICK_HI 0.2
+#endif
+#ifndef NK_GLFW_MAX_TEXTURES
+#define NK_GLFW_MAX_TEXTURES 256
+#endif
+
+#define VK_COLOR_COMPONENT_MASK_RGBA                                           \
+    VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |                      \
+        VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT
+
+struct nk_glfw_vertex {
+    float position[2];
+    float uv[2];
+    nk_byte col[4];
+};
+
+struct nk_vulkan_texture_descriptor_set {
+    VkImageView image_view;
+    VkDescriptorSet descriptor_set;
+};
+
+struct nk_glfw_device {
+    struct nk_buffer cmds;
+    struct nk_draw_null_texture tex_null;
+    int max_vertex_buffer;
+    int max_element_buffer;
+    VkDevice logical_device;
+    VkPhysicalDevice physical_device;
+    VkImageView *image_views;
+    uint32_t image_views_len;
+    VkFormat color_format;
+    VkFramebuffer *framebuffers;
+    uint32_t framebuffers_len;
+    VkCommandBuffer *command_buffers;
+    uint32_t command_buffers_len;
+    VkSampler sampler;
+    VkCommandPool command_pool;
+    VkSemaphore render_completed;
+    VkBuffer vertex_buffer;
+    VkDeviceMemory vertex_memory;
+    void *mapped_vertex;
+    VkBuffer index_buffer;
+    VkDeviceMemory index_memory;
+    void *mapped_index;
+    VkBuffer uniform_buffer;
+    VkDeviceMemory uniform_memory;
+    void *mapped_uniform;
+    VkRenderPass render_pass;
+    VkDescriptorPool descriptor_pool;
+    VkDescriptorSetLayout uniform_descriptor_set_layout;
+    VkDescriptorSet uniform_descriptor_set;
+    VkDescriptorSetLayout texture_descriptor_set_layout;
+    struct nk_vulkan_texture_descriptor_set *texture_descriptor_sets;
+    uint32_t texture_descriptor_sets_len;
+    VkPipelineLayout pipeline_layout;
+    VkPipeline pipeline;
+    VkImage font_image;
+    VkImageView font_image_view;
+    VkDeviceMemory font_memory;
+};
+
+static struct nk_glfw {
+    GLFWwindow *win;
+    int width, height;
+    int display_width, display_height;
+    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];
+    int text_len;
+    struct nk_vec2 scroll;
+    double last_button_click;
+    int is_double_click_down;
+    struct nk_vec2 double_click_pos;
+} glfw;
+
+struct Mat4f {
+    float m[16];
+};
+
+NK_INTERN uint32_t nk_glfw3_find_memory_index(
+    VkPhysicalDevice physical_device, uint32_t type_filter,
+    VkMemoryPropertyFlags properties) {
+    VkPhysicalDeviceMemoryProperties mem_properties;
+    uint32_t i;
+
+    vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_properties);
+    for (i = 0; i < mem_properties.memoryTypeCount; i++) {
+        if ((type_filter & (1 << i)) &&
+            (mem_properties.memoryTypes[i].propertyFlags & properties) ==
+                properties) {
+            return i;
+        }
+    }
+
+    assert(0);
+    return 0;
+}
+
+NK_INTERN void nk_glfw3_create_sampler(struct nk_glfw_device *dev) {
+    VkResult result;
+    VkSamplerCreateInfo sampler_info;
+    memset(&sampler_info, 0, sizeof(VkSamplerCreateInfo));
+
+    sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+    sampler_info.pNext = NULL;
+    sampler_info.maxAnisotropy = 1.0;
+    sampler_info.magFilter = VK_FILTER_LINEAR;
+    sampler_info.minFilter = VK_FILTER_LINEAR;
+    sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+    sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.mipLodBias = 0.0f;
+    sampler_info.compareEnable = VK_FALSE;
+    sampler_info.compareOp = VK_COMPARE_OP_ALWAYS;
+    sampler_info.minLod = 0.0f;
+    sampler_info.maxLod = 0.0f;
+    sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
+
+    result = vkCreateSampler(dev->logical_device, &sampler_info, NULL,
+                             &dev->sampler);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void
+nk_glfw3_create_command_pool(struct nk_glfw_device *dev,
+                             uint32_t graphics_queue_family_index) {
+    VkResult result;
+    VkCommandPoolCreateInfo pool_info;
+    memset(&pool_info, 0, sizeof(VkCommandPoolCreateInfo));
+
+    pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+    pool_info.queueFamilyIndex = graphics_queue_family_index;
+    pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+    result = vkCreateCommandPool(dev->logical_device, &pool_info, NULL,
+                                 &dev->command_pool);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_command_buffers(struct nk_glfw_device *dev) {
+    VkResult result;
+    VkCommandBufferAllocateInfo allocate_info;
+    memset(&allocate_info, 0, sizeof(VkCommandBufferAllocateInfo));
+
+    dev->command_buffers = (VkCommandBuffer *)malloc(dev->image_views_len *
+                                                     sizeof(VkCommandBuffer));
+    dev->command_buffers_len = dev->image_views_len;
+
+    allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    allocate_info.commandPool = dev->command_pool;
+    allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    allocate_info.commandBufferCount = dev->command_buffers_len;
+
+    result = vkAllocateCommandBuffers(dev->logical_device, &allocate_info,
+                                      dev->command_buffers);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_semaphore(struct nk_glfw_device *dev) {
+    VkResult result;
+    VkSemaphoreCreateInfo semaphore_info;
+    memset(&semaphore_info, 0, sizeof(VkSemaphoreCreateInfo));
+
+    semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+    result = (vkCreateSemaphore(dev->logical_device, &semaphore_info, NULL,
+                                &dev->render_completed));
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_buffer_and_memory(struct nk_glfw_device *dev,
+                                                 VkBuffer *buffer,
+                                                 VkBufferUsageFlags usage,
+                                                 VkDeviceMemory *memory,
+                                                 VkDeviceSize size) {
+    VkMemoryRequirements mem_reqs;
+    VkResult result;
+    VkBufferCreateInfo buffer_info;
+    VkMemoryAllocateInfo alloc_info;
+
+    memset(&buffer_info, 0, sizeof(VkBufferCreateInfo));
+    buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+    buffer_info.size = size;
+    buffer_info.usage = usage;
+    buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+    result = vkCreateBuffer(dev->logical_device, &buffer_info, NULL, buffer);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    vkGetBufferMemoryRequirements(dev->logical_device, *buffer, &mem_reqs);
+
+    memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo));
+    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+    alloc_info.allocationSize = mem_reqs.size;
+    alloc_info.memoryTypeIndex = nk_glfw3_find_memory_index(
+        dev->physical_device, mem_reqs.memoryTypeBits,
+        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+            VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+    result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL, memory);
+    NK_ASSERT(result == VK_SUCCESS);
+    result = vkBindBufferMemory(dev->logical_device, *buffer, *memory, 0);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_render_pass(struct nk_glfw_device *dev) {
+    VkAttachmentDescription attachment;
+    VkAttachmentReference color_reference;
+    VkSubpassDependency subpass_dependency;
+    VkSubpassDescription subpass_description;
+    VkRenderPassCreateInfo render_pass_info;
+    VkResult result;
+
+    memset(&attachment, 0, sizeof(VkAttachmentDescription));
+    attachment.format = dev->color_format;
+    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+    attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+    attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+    attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    attachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+    memset(&color_reference, 0, sizeof(VkAttachmentReference));
+    color_reference.attachment = 0;
+    color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+    memset(&subpass_dependency, 0, sizeof(VkSubpassDependency));
+    subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+    subpass_dependency.srcAccessMask = 0;
+    subpass_dependency.srcStageMask =
+        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    subpass_dependency.dstSubpass = 0;
+    subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+                                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+    subpass_dependency.dstStageMask =
+        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+
+    memset(&subpass_description, 0, sizeof(VkSubpassDescription));
+    subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+    subpass_description.colorAttachmentCount = 1;
+    subpass_description.pColorAttachments = &color_reference;
+
+    memset(&render_pass_info, 0, sizeof(VkRenderPassCreateInfo));
+    render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+    render_pass_info.attachmentCount = 1;
+    render_pass_info.pAttachments = &attachment;
+    render_pass_info.subpassCount = 1;
+    render_pass_info.pSubpasses = &subpass_description;
+    render_pass_info.dependencyCount = 1;
+    render_pass_info.pDependencies = &subpass_dependency;
+
+    result = vkCreateRenderPass(dev->logical_device, &render_pass_info, NULL,
+                                &dev->render_pass);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_framebuffers(struct nk_glfw_device *dev,
+                                            uint32_t framebuffer_width,
+                                            uint32_t framebuffer_height) {
+
+    VkFramebufferCreateInfo framebuffer_create_info;
+    uint32_t i;
+    VkResult result;
+
+    dev->framebuffers =
+        (VkFramebuffer *)malloc(dev->image_views_len * sizeof(VkFramebuffer));
+
+    memset(&framebuffer_create_info, 0, sizeof(VkFramebufferCreateInfo));
+    framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+    framebuffer_create_info.renderPass = dev->render_pass;
+    framebuffer_create_info.attachmentCount = 1;
+    framebuffer_create_info.width = framebuffer_width;
+    framebuffer_create_info.height = framebuffer_height;
+    framebuffer_create_info.layers = 1;
+    for (i = 0; i < dev->image_views_len; i++) {
+        framebuffer_create_info.pAttachments = &dev->image_views[i];
+        result =
+            vkCreateFramebuffer(dev->logical_device, &framebuffer_create_info,
+                                NULL, &dev->framebuffers[i]);
+        NK_ASSERT(result == VK_SUCCESS);
+    }
+    dev->framebuffers_len = dev->image_views_len;
+}
+
+NK_INTERN void nk_glfw3_create_descriptor_pool(struct nk_glfw_device *dev) {
+    VkDescriptorPoolSize pool_sizes[2];
+    VkDescriptorPoolCreateInfo pool_info;
+    VkResult result;
+
+    memset(&pool_sizes, 0, sizeof(VkDescriptorPoolSize) * 2);
+    pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    pool_sizes[0].descriptorCount = 1;
+    pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    pool_sizes[1].descriptorCount = NK_GLFW_MAX_TEXTURES;
+
+    memset(&pool_info, 0, sizeof(VkDescriptorPoolCreateInfo));
+    pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+    pool_info.poolSizeCount = 2;
+    pool_info.pPoolSizes = pool_sizes;
+    pool_info.maxSets = 1 + NK_GLFW_MAX_TEXTURES;
+
+    result = vkCreateDescriptorPool(dev->logical_device, &pool_info, NULL,
+                                    &dev->descriptor_pool);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void
+nk_glfw3_create_uniform_descriptor_set_layout(struct nk_glfw_device *dev) {
+    VkDescriptorSetLayoutBinding binding;
+    VkDescriptorSetLayoutCreateInfo descriptor_set_info;
+    VkResult result;
+
+    memset(&binding, 0, sizeof(VkDescriptorSetLayoutBinding));
+    binding.binding = 0;
+    binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    binding.descriptorCount = 1;
+    binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+
+    memset(&descriptor_set_info, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
+    descriptor_set_info.sType =
+        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+    descriptor_set_info.bindingCount = 1;
+    descriptor_set_info.pBindings = &binding;
+
+    result =
+        vkCreateDescriptorSetLayout(dev->logical_device, &descriptor_set_info,
+                                    NULL, &dev->uniform_descriptor_set_layout);
+
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void
+nk_glfw3_create_and_update_uniform_descriptor_set(struct nk_glfw_device *dev) {
+    VkDescriptorSetAllocateInfo allocate_info;
+    VkDescriptorBufferInfo buffer_info;
+    VkWriteDescriptorSet descriptor_write;
+    VkResult result;
+
+    memset(&allocate_info, 0, sizeof(VkDescriptorSetAllocateInfo));
+    allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    allocate_info.descriptorPool = dev->descriptor_pool;
+    allocate_info.descriptorSetCount = 1;
+    allocate_info.pSetLayouts = &dev->uniform_descriptor_set_layout;
+
+    result = vkAllocateDescriptorSets(dev->logical_device, &allocate_info,
+                                      &dev->uniform_descriptor_set);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&buffer_info, 0, sizeof(VkDescriptorBufferInfo));
+    buffer_info.buffer = dev->uniform_buffer;
+    buffer_info.offset = 0;
+    buffer_info.range = sizeof(struct Mat4f);
+
+    memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet));
+    descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    descriptor_write.dstSet = dev->uniform_descriptor_set;
+    descriptor_write.dstBinding = 0;
+    descriptor_write.dstArrayElement = 0;
+    descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    descriptor_write.descriptorCount = 1;
+    descriptor_write.pBufferInfo = &buffer_info;
+
+    vkUpdateDescriptorSets(dev->logical_device, 1, &descriptor_write, 0, NULL);
+}
+
+NK_INTERN void
+nk_glfw3_create_texture_descriptor_set_layout(struct nk_glfw_device *dev) {
+    VkDescriptorSetLayoutBinding binding;
+    VkDescriptorSetLayoutCreateInfo descriptor_set_info;
+    VkResult result;
+
+    memset(&binding, 0, sizeof(VkDescriptorSetLayoutBinding));
+    binding.binding = 0;
+    binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    binding.descriptorCount = 1;
+    binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+
+    memset(&descriptor_set_info, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
+    descriptor_set_info.sType =
+        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+    descriptor_set_info.bindingCount = 1;
+    descriptor_set_info.pBindings = &binding;
+
+    result =
+        vkCreateDescriptorSetLayout(dev->logical_device, &descriptor_set_info,
+                                    NULL, &dev->texture_descriptor_set_layout);
+
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void
+nk_glfw3_create_texture_descriptor_sets(struct nk_glfw_device *dev) {
+    VkDescriptorSetLayout *descriptor_set_layouts;
+    VkDescriptorSet *descriptor_sets;
+    VkDescriptorSetAllocateInfo allocate_info;
+    VkResult result;
+    int i;
+
+    descriptor_set_layouts = (VkDescriptorSetLayout *)malloc(
+        NK_GLFW_MAX_TEXTURES * sizeof(VkDescriptorSetLayout));
+    descriptor_sets = (VkDescriptorSet *)malloc(NK_GLFW_MAX_TEXTURES *
+                                                sizeof(VkDescriptorSet));
+
+    dev->texture_descriptor_sets =
+        (struct nk_vulkan_texture_descriptor_set *)malloc(
+            NK_GLFW_MAX_TEXTURES *
+            sizeof(struct nk_vulkan_texture_descriptor_set));
+    dev->texture_descriptor_sets_len = 0;
+
+    for (i = 0; i < NK_GLFW_MAX_TEXTURES; i++) {
+        descriptor_set_layouts[i] = dev->texture_descriptor_set_layout;
+        descriptor_sets[i] = dev->texture_descriptor_sets[i].descriptor_set;
+    }
+
+    memset(&allocate_info, 0, sizeof(VkDescriptorSetAllocateInfo));
+    allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    allocate_info.descriptorPool = dev->descriptor_pool;
+    allocate_info.descriptorSetCount = NK_GLFW_MAX_TEXTURES;
+    allocate_info.pSetLayouts = descriptor_set_layouts;
+
+    result = vkAllocateDescriptorSets(dev->logical_device, &allocate_info,
+                                      descriptor_sets);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    for (i = 0; i < NK_GLFW_MAX_TEXTURES; i++) {
+        dev->texture_descriptor_sets[i].descriptor_set = descriptor_sets[i];
+    }
+    free(descriptor_set_layouts);
+    free(descriptor_sets);
+}
+
+NK_INTERN void nk_glfw3_create_pipeline_layout(struct nk_glfw_device *dev) {
+    VkPipelineLayoutCreateInfo pipeline_layout_info;
+    VkDescriptorSetLayout descriptor_set_layouts[2];
+    VkResult result;
+
+    descriptor_set_layouts[0] = dev->uniform_descriptor_set_layout;
+    descriptor_set_layouts[1] = dev->texture_descriptor_set_layout;
+
+    memset(&pipeline_layout_info, 0, sizeof(VkPipelineLayoutCreateInfo));
+    pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+    pipeline_layout_info.setLayoutCount = 2;
+    pipeline_layout_info.pSetLayouts = descriptor_set_layouts;
+
+    result = (vkCreatePipelineLayout(dev->logical_device, &pipeline_layout_info,
+                                     NULL, &dev->pipeline_layout));
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN VkPipelineShaderStageCreateInfo
+nk_glfw3_create_shader(struct nk_glfw_device *dev, unsigned char *spv_shader,
+                       uint32_t size, VkShaderStageFlagBits stage_bit) {
+    VkShaderModuleCreateInfo create_info;
+    VkPipelineShaderStageCreateInfo shader_info;
+    VkShaderModule module = NULL;
+    VkResult result;
+
+    memset(&create_info, 0, sizeof(VkShaderModuleCreateInfo));
+    create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+    create_info.codeSize = size;
+    create_info.pCode = (const uint32_t *)spv_shader;
+    result =
+        vkCreateShaderModule(dev->logical_device, &create_info, NULL, &module);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&shader_info, 0, sizeof(VkPipelineShaderStageCreateInfo));
+    shader_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+    shader_info.stage = stage_bit;
+    shader_info.module = module;
+    shader_info.pName = "main";
+    return shader_info;
+}
+
+NK_INTERN void nk_glfw3_create_pipeline(struct nk_glfw_device *dev) {
+    VkPipelineInputAssemblyStateCreateInfo input_assembly_state;
+    VkPipelineRasterizationStateCreateInfo rasterization_state;
+    VkPipelineColorBlendAttachmentState attachment_state = {
+        VK_TRUE,
+        VK_BLEND_FACTOR_SRC_ALPHA,
+        VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+        VK_BLEND_OP_ADD,
+        VK_BLEND_FACTOR_SRC_ALPHA,
+        VK_BLEND_FACTOR_ONE,
+        VK_BLEND_OP_ADD,
+        VK_COLOR_COMPONENT_MASK_RGBA,
+    };
+    VkPipelineColorBlendStateCreateInfo color_blend_state;
+    VkPipelineViewportStateCreateInfo viewport_state;
+    VkPipelineMultisampleStateCreateInfo multisample_state;
+    VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT,
+                                        VK_DYNAMIC_STATE_SCISSOR};
+    VkPipelineDynamicStateCreateInfo dynamic_state;
+    VkPipelineShaderStageCreateInfo shader_stages[2];
+    VkVertexInputBindingDescription vertex_input_info;
+    VkVertexInputAttributeDescription vertex_attribute_description[3];
+    VkPipelineVertexInputStateCreateInfo vertex_input;
+    VkGraphicsPipelineCreateInfo pipeline_info;
+    VkResult result;
+
+    memset(&input_assembly_state, 0,
+           sizeof(VkPipelineInputAssemblyStateCreateInfo));
+    input_assembly_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+    input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+    input_assembly_state.primitiveRestartEnable = VK_FALSE;
+
+    memset(&rasterization_state, 0,
+           sizeof(VkPipelineRasterizationStateCreateInfo));
+    rasterization_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+    rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
+    rasterization_state.cullMode = VK_CULL_MODE_NONE;
+    rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+    rasterization_state.lineWidth = 1.0f;
+
+    memset(&color_blend_state, 0, sizeof(VkPipelineColorBlendStateCreateInfo));
+    color_blend_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+    color_blend_state.attachmentCount = 1;
+    color_blend_state.pAttachments = &attachment_state;
+
+    memset(&viewport_state, 0, sizeof(VkPipelineViewportStateCreateInfo));
+    viewport_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+    viewport_state.viewportCount = 1;
+    viewport_state.scissorCount = 1;
+
+    memset(&multisample_state, 0, sizeof(VkPipelineMultisampleStateCreateInfo));
+    multisample_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+    multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+    memset(&dynamic_state, 0, sizeof(VkPipelineDynamicStateCreateInfo));
+    dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+    dynamic_state.pDynamicStates = dynamic_states;
+    dynamic_state.dynamicStateCount = 2;
+
+    shader_stages[0] = nk_glfw3_create_shader(
+        dev, nuklearshaders_nuklear_vert_spv,
+        nuklearshaders_nuklear_vert_spv_len, VK_SHADER_STAGE_VERTEX_BIT);
+    shader_stages[1] = nk_glfw3_create_shader(
+        dev, nuklearshaders_nuklear_frag_spv,
+        nuklearshaders_nuklear_frag_spv_len, VK_SHADER_STAGE_FRAGMENT_BIT);
+
+    memset(&vertex_input_info, 0, sizeof(VkVertexInputBindingDescription));
+    vertex_input_info.binding = 0;
+    vertex_input_info.stride = sizeof(struct nk_glfw_vertex);
+    vertex_input_info.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+    memset(&vertex_attribute_description, 0,
+           sizeof(VkVertexInputAttributeDescription) * 3);
+    vertex_attribute_description[0].location = 0;
+    vertex_attribute_description[0].format = VK_FORMAT_R32G32_SFLOAT;
+    vertex_attribute_description[0].offset =
+        NK_OFFSETOF(struct nk_glfw_vertex, position);
+    vertex_attribute_description[1].location = 1;
+    vertex_attribute_description[1].format = VK_FORMAT_R32G32_SFLOAT;
+    vertex_attribute_description[1].offset =
+        NK_OFFSETOF(struct nk_glfw_vertex, uv);
+    vertex_attribute_description[2].location = 2;
+    vertex_attribute_description[2].format = VK_FORMAT_R8G8B8A8_UINT;
+    vertex_attribute_description[2].offset =
+        NK_OFFSETOF(struct nk_glfw_vertex, col);
+
+    memset(&vertex_input, 0, sizeof(VkPipelineVertexInputStateCreateInfo));
+    vertex_input.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+    vertex_input.vertexBindingDescriptionCount = 1;
+    vertex_input.pVertexBindingDescriptions = &vertex_input_info;
+    vertex_input.vertexAttributeDescriptionCount = 3;
+    vertex_input.pVertexAttributeDescriptions = vertex_attribute_description;
+
+    memset(&pipeline_info, 0, sizeof(VkGraphicsPipelineCreateInfo));
+    pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+    pipeline_info.flags = 0;
+    pipeline_info.stageCount = 2;
+    pipeline_info.pStages = shader_stages;
+    pipeline_info.pVertexInputState = &vertex_input;
+    pipeline_info.pInputAssemblyState = &input_assembly_state;
+    pipeline_info.pViewportState = &viewport_state;
+    pipeline_info.pRasterizationState = &rasterization_state;
+    pipeline_info.pMultisampleState = &multisample_state;
+    pipeline_info.pColorBlendState = &color_blend_state;
+    pipeline_info.pDynamicState = &dynamic_state;
+    pipeline_info.layout = dev->pipeline_layout;
+    pipeline_info.renderPass = dev->render_pass;
+    pipeline_info.basePipelineIndex = -1;
+    pipeline_info.basePipelineHandle = NULL;
+
+    result = vkCreateGraphicsPipelines(dev->logical_device, NULL, 1,
+                                       &pipeline_info, NULL, &dev->pipeline);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    vkDestroyShaderModule(dev->logical_device, shader_stages[0].module, NULL);
+    vkDestroyShaderModule(dev->logical_device, shader_stages[1].module, NULL);
+}
+
+NK_INTERN void nk_glfw3_create_render_resources(struct nk_glfw_device *dev,
+                                                uint32_t framebuffer_width,
+                                                uint32_t framebuffer_height) {
+    nk_glfw3_create_render_pass(dev);
+    nk_glfw3_create_framebuffers(dev, framebuffer_width, framebuffer_height);
+    nk_glfw3_create_descriptor_pool(dev);
+    nk_glfw3_create_uniform_descriptor_set_layout(dev);
+    nk_glfw3_create_and_update_uniform_descriptor_set(dev);
+    nk_glfw3_create_texture_descriptor_set_layout(dev);
+    nk_glfw3_create_texture_descriptor_sets(dev);
+    nk_glfw3_create_pipeline_layout(dev);
+    nk_glfw3_create_pipeline(dev);
+}
+
+NK_API void nk_glfw3_device_create(
+    VkDevice logical_device, VkPhysicalDevice physical_device,
+    uint32_t graphics_queue_family_index, VkImageView *image_views,
+    uint32_t image_views_len, VkFormat color_format,
+    VkDeviceSize max_vertex_buffer, VkDeviceSize max_element_buffer,
+    uint32_t framebuffer_width, uint32_t framebuffer_height) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+    dev->max_vertex_buffer = max_vertex_buffer;
+    dev->max_element_buffer = max_element_buffer;
+    nk_buffer_init_default(&dev->cmds);
+    dev->logical_device = logical_device;
+    dev->physical_device = physical_device;
+    dev->image_views = image_views;
+    dev->image_views_len = image_views_len;
+    dev->color_format = color_format;
+    dev->framebuffers = NULL;
+    dev->framebuffers_len = 0;
+
+    nk_glfw3_create_sampler(dev);
+    nk_glfw3_create_command_pool(dev, graphics_queue_family_index);
+    nk_glfw3_create_command_buffers(dev);
+    nk_glfw3_create_semaphore(dev);
+
+    nk_glfw3_create_buffer_and_memory(dev, &dev->vertex_buffer,
+                                      VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+                                      &dev->vertex_memory, max_vertex_buffer);
+    nk_glfw3_create_buffer_and_memory(dev, &dev->index_buffer,
+                                      VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+                                      &dev->index_memory, max_element_buffer);
+    nk_glfw3_create_buffer_and_memory(
+        dev, &dev->uniform_buffer, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+        &dev->uniform_memory, sizeof(struct Mat4f));
+
+    vkMapMemory(dev->logical_device, dev->vertex_memory, 0, max_vertex_buffer,
+                0, &dev->mapped_vertex);
+    vkMapMemory(dev->logical_device, dev->index_memory, 0, max_element_buffer,
+                0, &dev->mapped_index);
+    vkMapMemory(dev->logical_device, dev->uniform_memory, 0,
+                sizeof(struct Mat4f), 0, &dev->mapped_uniform);
+
+    nk_glfw3_create_render_resources(dev, framebuffer_width,
+                                     framebuffer_height);
+}
+
+NK_INTERN void nk_glfw3_device_upload_atlas(VkQueue graphics_queue,
+                                            const void *image, int width,
+                                            int height) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+
+    VkImageCreateInfo image_info;
+    VkResult result;
+    VkMemoryRequirements mem_reqs;
+    VkMemoryAllocateInfo alloc_info;
+    VkBufferCreateInfo buffer_info;
+    uint8_t *data = 0;
+    VkCommandBufferBeginInfo begin_info;
+    VkCommandBuffer command_buffer;
+    VkImageMemoryBarrier image_memory_barrier;
+    VkBufferImageCopy buffer_copy_region;
+    VkImageMemoryBarrier image_shader_memory_barrier;
+    VkFence fence;
+    VkFenceCreateInfo fence_create;
+    VkSubmitInfo submit_info;
+    VkImageViewCreateInfo image_view_info;
+    struct {
+        VkDeviceMemory memory;
+        VkBuffer buffer;
+    } staging_buffer;
+
+    memset(&image_info, 0, sizeof(VkImageCreateInfo));
+    image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    image_info.imageType = VK_IMAGE_TYPE_2D;
+    image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
+    image_info.extent.width = (uint32_t)width;
+    image_info.extent.height = (uint32_t)height;
+    image_info.extent.depth = 1;
+    image_info.mipLevels = 1;
+    image_info.arrayLayers = 1;
+    image_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+    image_info.usage =
+        VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+    image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    result =
+        vkCreateImage(dev->logical_device, &image_info, NULL, &dev->font_image);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    vkGetImageMemoryRequirements(dev->logical_device, dev->font_image,
+                                 &mem_reqs);
+
+    memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo));
+    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+    alloc_info.allocationSize = mem_reqs.size;
+    alloc_info.memoryTypeIndex = nk_glfw3_find_memory_index(
+        dev->physical_device, mem_reqs.memoryTypeBits,
+        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+    result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL,
+                              &dev->font_memory);
+    NK_ASSERT(result == VK_SUCCESS);
+    result = vkBindImageMemory(dev->logical_device, dev->font_image,
+                               dev->font_memory, 0);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&buffer_info, 0, sizeof(VkBufferCreateInfo));
+    buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+    buffer_info.size = alloc_info.allocationSize;
+    buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+    buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+    result = vkCreateBuffer(dev->logical_device, &buffer_info, NULL,
+                            &staging_buffer.buffer);
+    NK_ASSERT(result == VK_SUCCESS);
+    vkGetBufferMemoryRequirements(dev->logical_device, staging_buffer.buffer,
+                                  &mem_reqs);
+
+    alloc_info.allocationSize = mem_reqs.size;
+    alloc_info.memoryTypeIndex = nk_glfw3_find_memory_index(
+        dev->physical_device, mem_reqs.memoryTypeBits,
+        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+            VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+    result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL,
+                              &staging_buffer.memory);
+    NK_ASSERT(result == VK_SUCCESS);
+    result = vkBindBufferMemory(dev->logical_device, staging_buffer.buffer,
+                                staging_buffer.memory, 0);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    result = vkMapMemory(dev->logical_device, staging_buffer.memory, 0,
+                         alloc_info.allocationSize, 0, (void **)&data);
+    NK_ASSERT(result == VK_SUCCESS);
+    memcpy(data, image, width * height * 4);
+    vkUnmapMemory(dev->logical_device, staging_buffer.memory);
+
+    memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo));
+    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+    NK_ASSERT(dev->command_buffers_len > 0);
+    /*
+    use the same command buffer as for render as we are regenerating the
+    buffer during render anyway
+    */
+    command_buffer = dev->command_buffers[0];
+    result = vkBeginCommandBuffer(command_buffer, &begin_info);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&image_memory_barrier, 0, sizeof(VkImageMemoryBarrier));
+    image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    image_memory_barrier.image = dev->font_image;
+    image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+    image_memory_barrier.subresourceRange.aspectMask =
+        VK_IMAGE_ASPECT_COLOR_BIT;
+    image_memory_barrier.subresourceRange.levelCount = 1;
+    image_memory_barrier.subresourceRange.layerCount = 1;
+    image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+                         VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
+                         &image_memory_barrier);
+
+    memset(&buffer_copy_region, 0, sizeof(VkBufferImageCopy));
+    buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    buffer_copy_region.imageSubresource.layerCount = 1;
+    buffer_copy_region.imageExtent.width = (uint32_t)width;
+    buffer_copy_region.imageExtent.height = (uint32_t)height;
+    buffer_copy_region.imageExtent.depth = 1;
+
+    vkCmdCopyBufferToImage(
+        command_buffer, staging_buffer.buffer, dev->font_image,
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_copy_region);
+
+    memset(&image_shader_memory_barrier, 0, sizeof(VkImageMemoryBarrier));
+    image_shader_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    image_shader_memory_barrier.image = dev->font_image;
+    image_shader_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_shader_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_shader_memory_barrier.oldLayout =
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+    image_shader_memory_barrier.newLayout =
+        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+    image_shader_memory_barrier.subresourceRange.aspectMask =
+        VK_IMAGE_ASPECT_COLOR_BIT;
+    image_shader_memory_barrier.subresourceRange.levelCount = 1;
+    image_shader_memory_barrier.subresourceRange.layerCount = 1;
+    image_shader_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+    image_shader_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
+                         VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0,
+                         NULL, 1, &image_shader_memory_barrier);
+
+    result = vkEndCommandBuffer(command_buffer);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&fence_create, 0, sizeof(VkFenceCreateInfo));
+    fence_create.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+
+    result = vkCreateFence(dev->logical_device, &fence_create, NULL, &fence);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&submit_info, 0, sizeof(VkSubmitInfo));
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &command_buffer;
+
+    result = vkQueueSubmit(graphics_queue, 1, &submit_info, fence);
+    NK_ASSERT(result == VK_SUCCESS);
+    result =
+        vkWaitForFences(dev->logical_device, 1, &fence, VK_TRUE, UINT64_MAX);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    vkDestroyFence(dev->logical_device, fence, NULL);
+
+    vkFreeMemory(dev->logical_device, staging_buffer.memory, NULL);
+    vkDestroyBuffer(dev->logical_device, staging_buffer.buffer, NULL);
+
+    memset(&image_view_info, 0, sizeof(VkImageViewCreateInfo));
+    image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    image_view_info.image = dev->font_image;
+    image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    image_view_info.format = image_info.format;
+    image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    image_view_info.subresourceRange.layerCount = 1;
+    image_view_info.subresourceRange.levelCount = 1;
+
+    result = vkCreateImageView(dev->logical_device, &image_view_info, NULL,
+                               &dev->font_image_view);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_destroy_render_resources(struct nk_glfw_device *dev) {
+    uint32_t i;
+
+    vkDestroyPipeline(dev->logical_device, dev->pipeline, NULL);
+    vkDestroyPipelineLayout(dev->logical_device, dev->pipeline_layout, NULL);
+    vkDestroyDescriptorSetLayout(dev->logical_device,
+                                 dev->texture_descriptor_set_layout, NULL);
+    vkDestroyDescriptorSetLayout(dev->logical_device,
+                                 dev->uniform_descriptor_set_layout, NULL);
+    vkDestroyDescriptorPool(dev->logical_device, dev->descriptor_pool, NULL);
+    for (i = 0; i < dev->framebuffers_len; i++) {
+        vkDestroyFramebuffer(dev->logical_device, dev->framebuffers[i], NULL);
+    }
+    free(dev->framebuffers);
+    dev->framebuffers_len = 0;
+    free(dev->texture_descriptor_sets);
+    dev->texture_descriptor_sets_len = 0;
+    vkDestroyRenderPass(dev->logical_device, dev->render_pass, NULL);
+}
+
+NK_API void nk_glfw3_resize(uint32_t framebuffer_width,
+                            uint32_t framebuffer_height) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+    glfwGetWindowSize(glfw.win, &glfw.width, &glfw.height);
+    glfwGetFramebufferSize(glfw.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_glfw3_destroy_render_resources(dev);
+    nk_glfw3_create_render_resources(dev, framebuffer_width,
+                                     framebuffer_height);
+}
+
+NK_API void nk_glfw3_device_destroy(void) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+
+    vkDeviceWaitIdle(dev->logical_device);
+
+    nk_glfw3_destroy_render_resources(dev);
+
+    vkFreeCommandBuffers(dev->logical_device, dev->command_pool,
+                         dev->command_buffers_len, dev->command_buffers);
+    vkDestroyCommandPool(dev->logical_device, dev->command_pool, NULL);
+    vkDestroySemaphore(dev->logical_device, dev->render_completed, NULL);
+
+    vkUnmapMemory(dev->logical_device, dev->vertex_memory);
+    vkUnmapMemory(dev->logical_device, dev->index_memory);
+    vkUnmapMemory(dev->logical_device, dev->uniform_memory);
+
+    vkFreeMemory(dev->logical_device, dev->vertex_memory, NULL);
+    vkFreeMemory(dev->logical_device, dev->index_memory, NULL);
+    vkFreeMemory(dev->logical_device, dev->uniform_memory, NULL);
+
+    vkDestroyBuffer(dev->logical_device, dev->vertex_buffer, NULL);
+    vkDestroyBuffer(dev->logical_device, dev->index_buffer, NULL);
+    vkDestroyBuffer(dev->logical_device, dev->uniform_buffer, NULL);
+
+    vkDestroySampler(dev->logical_device, dev->sampler, NULL);
+
+    vkFreeMemory(dev->logical_device, dev->font_memory, NULL);
+    vkDestroyImage(dev->logical_device, dev->font_image, NULL);
+    vkDestroyImageView(dev->logical_device, dev->font_image_view, NULL);
+
+    free(dev->command_buffers);
+    nk_buffer_free(&dev->cmds);
+}
+
+NK_API
+void nk_glfw3_shutdown(void) {
+    nk_font_atlas_clear(&glfw.atlas);
+    nk_free(&glfw.ctx);
+    nk_glfw3_device_destroy();
+    memset(&glfw, 0, sizeof(glfw));
+}
+
+NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas) {
+    nk_font_atlas_init_default(&glfw.atlas);
+    nk_font_atlas_begin(&glfw.atlas);
+    *atlas = &glfw.atlas;
+}
+
+NK_API void nk_glfw3_font_stash_end(VkQueue graphics_queue) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+
+    const void *image;
+    int w, h;
+    image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
+    nk_glfw3_device_upload_atlas(graphics_queue, image, w, h);
+    nk_font_atlas_end(&glfw.atlas, nk_handle_ptr(dev->font_image_view),
+                      &dev->tex_null);
+    if (glfw.atlas.default_font) {
+        nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle);
+    }
+}
+
+NK_API void nk_glfw3_new_frame(void) {
+    int i;
+    double x, y;
+    struct nk_context *ctx = &glfw.ctx;
+    struct GLFWwindow *win = glfw.win;
+
+    nk_input_begin(ctx);
+    for (i = 0; i < glfw.text_len; ++i)
+        nk_input_unicode(ctx, glfw.text[i]);
+
+#ifdef NK_GLFW_GL4_MOUSE_GRABBING
+    /* optional grabbing behavior */
+    if (ctx->input.mouse.grab)
+        glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+    else if (ctx->input.mouse.ungrab)
+        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);
+    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);
+    } 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);
+        nk_input_key(ctx, NK_KEY_COPY, 0);
+        nk_input_key(ctx, NK_KEY_PASTE, 0);
+        nk_input_key(ctx, NK_KEY_CUT, 0);
+        nk_input_key(ctx, NK_KEY_SHIFT, 0);
+    }
+
+    glfwGetCursorPos(win, &x, &y);
+    nk_input_motion(ctx, (int)x, (int)y);
+#ifdef NK_GLFW_GL4_MOUSE_GRABBING
+    if (ctx->input.mouse.grabbed) {
+        glfwSetCursorPos(glfw.win, ctx->input.mouse.prev.x,
+                         ctx->input.mouse.prev.y);
+        ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
+        ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
+    }
+#endif
+    nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y,
+                    glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) ==
+                        GLFW_PRESS);
+    nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y,
+                    glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) ==
+                        GLFW_PRESS);
+    nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y,
+                    glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) ==
+                        GLFW_PRESS);
+    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);
+    glfw.text_len = 0;
+    glfw.scroll = nk_vec2(0, 0);
+}
+
+NK_INTERN void update_texture_descriptor_set(
+    struct nk_glfw_device *dev,
+    struct nk_vulkan_texture_descriptor_set *texture_descriptor_set,
+    VkImageView image_view) {
+    VkDescriptorImageInfo descriptor_image_info;
+    VkWriteDescriptorSet descriptor_write;
+
+    texture_descriptor_set->image_view = image_view;
+
+    memset(&descriptor_image_info, 0, sizeof(VkDescriptorImageInfo));
+    descriptor_image_info.sampler = dev->sampler;
+    descriptor_image_info.imageView = texture_descriptor_set->image_view;
+    descriptor_image_info.imageLayout =
+        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+    memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet));
+    descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    descriptor_write.dstSet = texture_descriptor_set->descriptor_set;
+    descriptor_write.dstBinding = 0;
+    descriptor_write.dstArrayElement = 0;
+    descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    descriptor_write.descriptorCount = 1;
+    descriptor_write.pImageInfo = &descriptor_image_info;
+
+    vkUpdateDescriptorSets(dev->logical_device, 1, &descriptor_write, 0, NULL);
+}
+
+NK_API
+VkSemaphore nk_glfw3_render(VkQueue graphics_queue, uint32_t buffer_index,
+                            VkSemaphore wait_semaphore,
+                            enum nk_anti_aliasing AA) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+    struct nk_buffer vbuf, ebuf;
+
+    struct Mat4f projection = {
+        {2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f,
+         0.0f, -1.0f, 1.0f, 0.0f, 1.0f},
+    };
+
+    VkCommandBufferBeginInfo begin_info;
+    VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 0.0f}}};
+    VkRenderPassBeginInfo render_pass_begin_nfo;
+    VkCommandBuffer command_buffer;
+    VkResult result;
+    VkViewport viewport;
+
+    VkDeviceSize doffset = 0;
+    VkImageView current_texture = NULL;
+    uint32_t index_offset = 0;
+    VkRect2D scissor;
+    uint32_t wait_semaphore_count;
+    VkSemaphore *wait_semaphores;
+    VkPipelineStageFlags wait_stage =
+        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    VkSubmitInfo submit_info;
+
+    projection.m[0] /= glfw.width;
+    projection.m[5] /= glfw.height;
+
+    memcpy(dev->mapped_uniform, &projection, sizeof(projection));
+
+    memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo));
+    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+    memset(&render_pass_begin_nfo, 0, sizeof(VkRenderPassBeginInfo));
+    render_pass_begin_nfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+    render_pass_begin_nfo.renderPass = dev->render_pass;
+    render_pass_begin_nfo.renderArea.extent.width = (uint32_t)glfw.width;
+    render_pass_begin_nfo.renderArea.extent.height = (uint32_t)glfw.height;
+    render_pass_begin_nfo.clearValueCount = 1;
+    render_pass_begin_nfo.pClearValues = &clear_value;
+    render_pass_begin_nfo.framebuffer = dev->framebuffers[buffer_index];
+
+    command_buffer = dev->command_buffers[buffer_index];
+
+    result = vkBeginCommandBuffer(command_buffer, &begin_info);
+    NK_ASSERT(result == VK_SUCCESS);
+    vkCmdBeginRenderPass(command_buffer, &render_pass_begin_nfo,
+                         VK_SUBPASS_CONTENTS_INLINE);
+
+    memset(&viewport, 0, sizeof(VkViewport));
+    viewport.width = (float)glfw.width;
+    viewport.height = (float)glfw.height;
+    viewport.maxDepth = 1.0f;
+    vkCmdSetViewport(command_buffer, 0, 1, &viewport);
+
+    vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                      dev->pipeline);
+    vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                            dev->pipeline_layout, 0, 1,
+                            &dev->uniform_descriptor_set, 0, NULL);
+    {
+        /* convert from command queue into draw list and draw to screen */
+        const struct nk_draw_command *cmd;
+        /* load draw vertices & elements directly into vertex + element buffer
+         */
+        {
+            /* fill convert configuration */
+            struct nk_convert_config config;
+            static const struct nk_draw_vertex_layout_element vertex_layout[] =
+                {{NK_VERTEX_POSITION, NK_FORMAT_FLOAT,
+                  NK_OFFSETOF(struct nk_glfw_vertex, position)},
+                 {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT,
+                  NK_OFFSETOF(struct nk_glfw_vertex, uv)},
+                 {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8,
+                  NK_OFFSETOF(struct nk_glfw_vertex, col)},
+                 {NK_VERTEX_LAYOUT_END}};
+            NK_MEMSET(&config, 0, sizeof(config));
+            config.vertex_layout = vertex_layout;
+            config.vertex_size = sizeof(struct nk_glfw_vertex);
+            config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex);
+            config.tex_null = dev->tex_null;
+            config.circle_segment_count = 22;
+            config.curve_segment_count = 22;
+            config.arc_segment_count = 22;
+            config.global_alpha = 1.0f;
+            config.shape_AA = AA;
+            config.line_AA = AA;
+
+            /* setup buffers to load vertices and elements */
+            nk_buffer_init_fixed(&vbuf, dev->mapped_vertex,
+                                 (size_t)dev->max_vertex_buffer);
+            nk_buffer_init_fixed(&ebuf, dev->mapped_index,
+                                 (size_t)dev->max_element_buffer);
+            nk_convert(&glfw.ctx, &dev->cmds, &vbuf, &ebuf, &config);
+        }
+
+        /* iterate over and execute each draw command */
+
+        vkCmdBindVertexBuffers(command_buffer, 0, 1, &dev->vertex_buffer,
+                               &doffset);
+        vkCmdBindIndexBuffer(command_buffer, dev->index_buffer, 0,
+                             VK_INDEX_TYPE_UINT16);
+
+        nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds) {
+            if (!cmd->texture.ptr) {
+                continue;
+            }
+            if (cmd->texture.ptr && cmd->texture.ptr != current_texture) {
+                int found = 0;
+                uint32_t i;
+                for (i = 0; i < dev->texture_descriptor_sets_len; i++) {
+                    if (dev->texture_descriptor_sets[i].image_view ==
+                        cmd->texture.ptr) {
+                        found = 1;
+                        break;
+                    }
+                }
+
+                if (!found) {
+                    update_texture_descriptor_set(
+                        dev, &dev->texture_descriptor_sets[i],
+                        (VkImageView)cmd->texture.ptr);
+                    dev->texture_descriptor_sets_len++;
+                }
+                vkCmdBindDescriptorSets(
+                    command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                    dev->pipeline_layout, 1, 1,
+                    &dev->texture_descriptor_sets[i].descriptor_set, 0, NULL);
+            }
+
+            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);
+            vkCmdSetScissor(command_buffer, 0, 1, &scissor);
+            vkCmdDrawIndexed(command_buffer, cmd->elem_count, 1, index_offset,
+                             0, 0);
+            index_offset += cmd->elem_count;
+        }
+        nk_clear(&glfw.ctx);
+    }
+
+    vkCmdEndRenderPass(command_buffer);
+    result = vkEndCommandBuffer(command_buffer);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    if (wait_semaphore) {
+        wait_semaphore_count = 1;
+        wait_semaphores = &wait_semaphore;
+    } else {
+        wait_semaphore_count = 0;
+        wait_semaphores = NULL;
+    }
+
+    memset(&submit_info, 0, sizeof(VkSubmitInfo));
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &command_buffer;
+    submit_info.pWaitDstStageMask = &wait_stage;
+    submit_info.waitSemaphoreCount = wait_semaphore_count;
+    submit_info.pWaitSemaphores = wait_semaphores;
+    submit_info.signalSemaphoreCount = 1;
+    submit_info.pSignalSemaphores = &dev->render_completed;
+
+    result = vkQueueSubmit(graphics_queue, 1, &submit_info, NULL);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    return dev->render_completed;
+}
+
+NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) {
+    (void)win;
+    if (glfw.text_len < NK_GLFW_TEXT_MAX)
+        glfw.text[glfw.text_len++] = codepoint;
+}
+
+NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff,
+                                     double yoff) {
+    (void)win;
+    (void)xoff;
+    glfw.scroll.x += (float)xoff;
+    glfw.scroll.y += (float)yoff;
+}
+
+NK_API void nk_glfw3_mouse_button_callback(GLFWwindow *window, int button,
+                                           int action, int mods) {
+    double x, y;
+    NK_UNUSED(mods);
+    if (button != GLFW_MOUSE_BUTTON_LEFT)
+        return;
+    glfwGetCursorPos(window, &x, &y);
+    if (action == GLFW_PRESS) {
+        double dt = glfwGetTime() - glfw.last_button_click;
+        if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) {
+            glfw.is_double_click_down = nk_true;
+            glfw.double_click_pos = nk_vec2((float)x, (float)y);
+        }
+        glfw.last_button_click = glfwGetTime();
+    } else
+        glfw.is_double_click_down = nk_false;
+}
+
+NK_INTERN void nk_glfw3_clipboard_paste(nk_handle usr,
+                                        struct nk_text_edit *edit) {
+    const char *text = glfwGetClipboardString(glfw.win);
+    if (text)
+        nk_textedit_paste(edit, text, nk_strlen(text));
+    (void)usr;
+}
+
+NK_INTERN void nk_glfw3_clipboard_copy(nk_handle usr, const char *text,
+                                       int len) {
+    char *str = 0;
+    (void)usr;
+    if (!len)
+        return;
+    str = (char *)malloc((size_t)len + 1);
+    if (!str)
+        return;
+    memcpy(str, text, (size_t)len);
+    str[len] = '\0';
+    glfwSetClipboardString(glfw.win, str);
+    free(str);
+}
+
+NK_API struct nk_context *
+nk_glfw3_init(GLFWwindow *win, VkDevice logical_device,
+              VkPhysicalDevice physical_device,
+              uint32_t graphics_queue_family_index, VkImageView *image_views,
+              uint32_t image_views_len, VkFormat color_format,
+              enum nk_glfw_init_state init_state,
+              VkDeviceSize max_vertex_buffer, VkDeviceSize max_element_buffer) {
+    memset(&glfw, 0, sizeof(struct nk_glfw));
+    glfw.win = win;
+    if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
+        glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
+        glfwSetCharCallback(win, nk_glfw3_char_callback);
+        glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
+    }
+    nk_init_default(&glfw.ctx, 0);
+    glfw.ctx.clip.copy = nk_glfw3_clipboard_copy;
+    glfw.ctx.clip.paste = nk_glfw3_clipboard_paste;
+    glfw.ctx.clip.userdata = nk_handle_ptr(0);
+    glfw.last_button_click = 0;
+
+    glfwGetWindowSize(win, &glfw.width, &glfw.height);
+    glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height);
+
+    nk_glfw3_device_create(logical_device, physical_device,
+                           graphics_queue_family_index, image_views,
+                           image_views_len, color_format, max_vertex_buffer,
+                           max_element_buffer, (uint32_t)glfw.display_width,
+                           (uint32_t)glfw.display_height);
+
+    glfw.is_double_click_down = nk_false;
+    glfw.double_click_pos = nk_vec2(0, 0);
+
+    return &glfw.ctx;
+}
+
+#endif

+ 12 - 0
demo/glfw_vulkan/shaders/demo.frag

@@ -0,0 +1,12 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(binding = 0) uniform sampler2D overlay;
+
+layout(location = 0) in vec2 inUV;
+
+layout(location = 0) out vec4 outColor;
+
+void main() {
+    outColor = texture(overlay, inUV);
+}

+ 10 - 0
demo/glfw_vulkan/shaders/demo.vert

@@ -0,0 +1,10 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+    outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+    gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
+}

+ 11 - 0
demo/glfw_vulkan/src/Makefile

@@ -0,0 +1,11 @@
+create_shader_inlined_header: nuklearshaders/nuklear.vert.spv nuklearshaders/nuklear.frag.spv
+	awk -v st='// NUKLEAR_SHADERS_START' -v et='// NUKLEAR_SHADERS_END' -v repl="$$(xxd -i nuklearshaders/nuklear.vert.spv && xxd -i nuklearshaders/nuklear.frag.spv)" '$$0 == st{del=1} $$0 == et{$$0 = repl; del=0} !del' nuklear_glfw_vulkan.in.h > nuklear_glfw_vulkan.h
+
+nuklearshaders/nuklear.vert.spv: nuklearshaders/nuklear.vert
+	glslc --target-env=vulkan nuklearshaders/nuklear.vert -o nuklearshaders/nuklear.vert.spv
+
+nuklearshaders/nuklear.frag.spv: nuklearshaders/nuklear.frag
+	glslc --target-env=vulkan nuklearshaders/nuklear.frag -o nuklearshaders/nuklear.frag.spv
+
+clean:
+	rm nuklearshaders/nuklear.vert.spv nuklearshaders/nuklear.frag.spv nuklear_glfw_vulkan.h

+ 5 - 0
demo/glfw_vulkan/src/README.md

@@ -0,0 +1,5 @@
+Contrary to OpenGL Vulkan needs precompiled shaders in the SPIR-V format which makes it a bit more difficult to inline the shadercode.
+
+After executing `make` the result should be a self contained `nuklear_glfw_vulkan.h`. Copy the result file to the parent directory and the "release" should be done.
+
+You will need to have `xxd`, `glslc` and `awk` installed for this.

+ 1426 - 0
demo/glfw_vulkan/src/nuklear_glfw_vulkan.in.h

@@ -0,0 +1,1426 @@
+/*
+ * Nuklear - 1.32.0 - public domain
+ * no warrenty implied; use at your own risk.
+ * authored from 2015-2016 by Micha Mettke
+ */
+/*
+ * ==============================================================
+ *
+ *                              API
+ *
+ * ===============================================================
+ */
+#ifndef NK_GLFW_VULKAN_H_
+#define NK_GLFW_VULKAN_H_
+
+// NUKLEAR_SHADERS_START
+// will be replaced with the real shader code
+// so we can have some ide support while editing the .in file
+#include "nuklear.h"
+
+unsigned char nuklearshaders_nuklear_vert_spv[] = {};
+unsigned int nuklearshaders_nuklear_vert_spv_len = 0;
+unsigned char nuklearshaders_nuklear_frag_spv[] = {};
+unsigned int nuklearshaders_nuklear_frag_spv_len = 0;
+// NUKLEAR_SHADERS_END
+
+#include <stddef.h>
+#include <string.h>
+#define GLFW_INCLUDE_VULKAN
+#include <GLFW/glfw3.h>
+
+enum nk_glfw_init_state { NK_GLFW3_DEFAULT = 0, NK_GLFW3_INSTALL_CALLBACKS };
+
+NK_API struct nk_context *
+nk_glfw3_init(GLFWwindow *win, VkDevice logical_device,
+              VkPhysicalDevice physical_device,
+              uint32_t graphics_queue_family_index, VkImageView *image_views,
+              uint32_t image_views_len, VkFormat color_format,
+              enum nk_glfw_init_state init_state,
+              VkDeviceSize max_vertex_buffer, VkDeviceSize max_element_buffer);
+NK_API void nk_glfw3_shutdown(void);
+NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas);
+NK_API void nk_glfw3_font_stash_end(VkQueue graphics_queue);
+NK_API void nk_glfw3_new_frame();
+NK_API VkSemaphore nk_glfw3_render(VkQueue graphics_queue,
+                                   uint32_t buffer_index,
+                                   VkSemaphore wait_semaphore,
+                                   enum nk_anti_aliasing AA);
+NK_API void nk_glfw3_resize(uint32_t framebuffer_width,
+                            uint32_t framebuffer_height);
+NK_API void nk_glfw3_device_destroy(void);
+NK_API void nk_glfw3_device_create(
+    VkDevice logical_device, VkPhysicalDevice physical_device,
+    uint32_t graphics_queue_family_index, VkImageView *image_views,
+    uint32_t image_views_len, VkFormat color_format,
+    VkDeviceSize max_vertex_buffer, VkDeviceSize max_element_buffer,
+    uint32_t framebuffer_width, uint32_t framebuffer_height);
+
+NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
+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);
+
+#endif
+/*
+ * ==============================================================
+ *
+ *                          IMPLEMENTATION
+ *
+ * ===============================================================
+ */
+#ifdef NK_GLFW_VULKAN_IMPLEMENTATION
+#undef NK_GLFW_VULKAN_IMPLEMENTATION
+#include <assert.h>
+#include <stdlib.h>
+
+#ifndef NK_GLFW_TEXT_MAX
+#define NK_GLFW_TEXT_MAX 256
+#endif
+#ifndef NK_GLFW_DOUBLE_CLICK_LO
+#define NK_GLFW_DOUBLE_CLICK_LO 0.02
+#endif
+#ifndef NK_GLFW_DOUBLE_CLICK_HI
+#define NK_GLFW_DOUBLE_CLICK_HI 0.2
+#endif
+#ifndef NK_GLFW_MAX_TEXTURES
+#define NK_GLFW_MAX_TEXTURES 256
+#endif
+
+#define VK_COLOR_COMPONENT_MASK_RGBA                                           \
+    VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |                      \
+        VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT
+
+struct nk_glfw_vertex {
+    float position[2];
+    float uv[2];
+    nk_byte col[4];
+};
+
+struct nk_vulkan_texture_descriptor_set {
+    VkImageView image_view;
+    VkDescriptorSet descriptor_set;
+};
+
+struct nk_glfw_device {
+    struct nk_buffer cmds;
+    struct nk_draw_null_texture tex_null;
+    int max_vertex_buffer;
+    int max_element_buffer;
+    VkDevice logical_device;
+    VkPhysicalDevice physical_device;
+    VkImageView *image_views;
+    uint32_t image_views_len;
+    VkFormat color_format;
+    VkFramebuffer *framebuffers;
+    uint32_t framebuffers_len;
+    VkCommandBuffer *command_buffers;
+    uint32_t command_buffers_len;
+    VkSampler sampler;
+    VkCommandPool command_pool;
+    VkSemaphore render_completed;
+    VkBuffer vertex_buffer;
+    VkDeviceMemory vertex_memory;
+    void *mapped_vertex;
+    VkBuffer index_buffer;
+    VkDeviceMemory index_memory;
+    void *mapped_index;
+    VkBuffer uniform_buffer;
+    VkDeviceMemory uniform_memory;
+    void *mapped_uniform;
+    VkRenderPass render_pass;
+    VkDescriptorPool descriptor_pool;
+    VkDescriptorSetLayout uniform_descriptor_set_layout;
+    VkDescriptorSet uniform_descriptor_set;
+    VkDescriptorSetLayout texture_descriptor_set_layout;
+    struct nk_vulkan_texture_descriptor_set *texture_descriptor_sets;
+    uint32_t texture_descriptor_sets_len;
+    VkPipelineLayout pipeline_layout;
+    VkPipeline pipeline;
+    VkImage font_image;
+    VkImageView font_image_view;
+    VkDeviceMemory font_memory;
+};
+
+static struct nk_glfw {
+    GLFWwindow *win;
+    int width, height;
+    int display_width, display_height;
+    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];
+    int text_len;
+    struct nk_vec2 scroll;
+    double last_button_click;
+    int is_double_click_down;
+    struct nk_vec2 double_click_pos;
+} glfw;
+
+struct Mat4f {
+    float m[16];
+};
+
+NK_INTERN uint32_t nk_glfw3_find_memory_index(
+    VkPhysicalDevice physical_device, uint32_t type_filter,
+    VkMemoryPropertyFlags properties) {
+    VkPhysicalDeviceMemoryProperties mem_properties;
+    uint32_t i;
+
+    vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_properties);
+    for (i = 0; i < mem_properties.memoryTypeCount; i++) {
+        if ((type_filter & (1 << i)) &&
+            (mem_properties.memoryTypes[i].propertyFlags & properties) ==
+                properties) {
+            return i;
+        }
+    }
+
+    assert(0);
+    return 0;
+}
+
+NK_INTERN void nk_glfw3_create_sampler(struct nk_glfw_device *dev) {
+    VkResult result;
+    VkSamplerCreateInfo sampler_info;
+    memset(&sampler_info, 0, sizeof(VkSamplerCreateInfo));
+
+    sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+    sampler_info.pNext = NULL;
+    sampler_info.maxAnisotropy = 1.0;
+    sampler_info.magFilter = VK_FILTER_LINEAR;
+    sampler_info.minFilter = VK_FILTER_LINEAR;
+    sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+    sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+    sampler_info.mipLodBias = 0.0f;
+    sampler_info.compareEnable = VK_FALSE;
+    sampler_info.compareOp = VK_COMPARE_OP_ALWAYS;
+    sampler_info.minLod = 0.0f;
+    sampler_info.maxLod = 0.0f;
+    sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
+
+    result = vkCreateSampler(dev->logical_device, &sampler_info, NULL,
+                             &dev->sampler);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void
+nk_glfw3_create_command_pool(struct nk_glfw_device *dev,
+                             uint32_t graphics_queue_family_index) {
+    VkResult result;
+    VkCommandPoolCreateInfo pool_info;
+    memset(&pool_info, 0, sizeof(VkCommandPoolCreateInfo));
+
+    pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+    pool_info.queueFamilyIndex = graphics_queue_family_index;
+    pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+    result = vkCreateCommandPool(dev->logical_device, &pool_info, NULL,
+                                 &dev->command_pool);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_command_buffers(struct nk_glfw_device *dev) {
+    VkResult result;
+    VkCommandBufferAllocateInfo allocate_info;
+    memset(&allocate_info, 0, sizeof(VkCommandBufferAllocateInfo));
+
+    dev->command_buffers = (VkCommandBuffer *)malloc(dev->image_views_len *
+                                                     sizeof(VkCommandBuffer));
+    dev->command_buffers_len = dev->image_views_len;
+
+    allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    allocate_info.commandPool = dev->command_pool;
+    allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    allocate_info.commandBufferCount = dev->command_buffers_len;
+
+    result = vkAllocateCommandBuffers(dev->logical_device, &allocate_info,
+                                      dev->command_buffers);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_semaphore(struct nk_glfw_device *dev) {
+    VkResult result;
+    VkSemaphoreCreateInfo semaphore_info;
+    memset(&semaphore_info, 0, sizeof(VkSemaphoreCreateInfo));
+
+    semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+    result = (vkCreateSemaphore(dev->logical_device, &semaphore_info, NULL,
+                                &dev->render_completed));
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_buffer_and_memory(struct nk_glfw_device *dev,
+                                                 VkBuffer *buffer,
+                                                 VkBufferUsageFlags usage,
+                                                 VkDeviceMemory *memory,
+                                                 VkDeviceSize size) {
+    VkMemoryRequirements mem_reqs;
+    VkResult result;
+    VkBufferCreateInfo buffer_info;
+    VkMemoryAllocateInfo alloc_info;
+
+    memset(&buffer_info, 0, sizeof(VkBufferCreateInfo));
+    buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+    buffer_info.size = size;
+    buffer_info.usage = usage;
+    buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+    result = vkCreateBuffer(dev->logical_device, &buffer_info, NULL, buffer);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    vkGetBufferMemoryRequirements(dev->logical_device, *buffer, &mem_reqs);
+
+    memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo));
+    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+    alloc_info.allocationSize = mem_reqs.size;
+    alloc_info.memoryTypeIndex = nk_glfw3_find_memory_index(
+        dev->physical_device, mem_reqs.memoryTypeBits,
+        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+            VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+    result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL, memory);
+    NK_ASSERT(result == VK_SUCCESS);
+    result = vkBindBufferMemory(dev->logical_device, *buffer, *memory, 0);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_render_pass(struct nk_glfw_device *dev) {
+    VkAttachmentDescription attachment;
+    VkAttachmentReference color_reference;
+    VkSubpassDependency subpass_dependency;
+    VkSubpassDescription subpass_description;
+    VkRenderPassCreateInfo render_pass_info;
+    VkResult result;
+
+    memset(&attachment, 0, sizeof(VkAttachmentDescription));
+    attachment.format = dev->color_format;
+    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+    attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+    attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+    attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    attachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+    memset(&color_reference, 0, sizeof(VkAttachmentReference));
+    color_reference.attachment = 0;
+    color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+    memset(&subpass_dependency, 0, sizeof(VkSubpassDependency));
+    subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+    subpass_dependency.srcAccessMask = 0;
+    subpass_dependency.srcStageMask =
+        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    subpass_dependency.dstSubpass = 0;
+    subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+                                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+    subpass_dependency.dstStageMask =
+        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+
+    memset(&subpass_description, 0, sizeof(VkSubpassDescription));
+    subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+    subpass_description.colorAttachmentCount = 1;
+    subpass_description.pColorAttachments = &color_reference;
+
+    memset(&render_pass_info, 0, sizeof(VkRenderPassCreateInfo));
+    render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+    render_pass_info.attachmentCount = 1;
+    render_pass_info.pAttachments = &attachment;
+    render_pass_info.subpassCount = 1;
+    render_pass_info.pSubpasses = &subpass_description;
+    render_pass_info.dependencyCount = 1;
+    render_pass_info.pDependencies = &subpass_dependency;
+
+    result = vkCreateRenderPass(dev->logical_device, &render_pass_info, NULL,
+                                &dev->render_pass);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_create_framebuffers(struct nk_glfw_device *dev,
+                                            uint32_t framebuffer_width,
+                                            uint32_t framebuffer_height) {
+
+    VkFramebufferCreateInfo framebuffer_create_info;
+    uint32_t i;
+    VkResult result;
+
+    dev->framebuffers =
+        (VkFramebuffer *)malloc(dev->image_views_len * sizeof(VkFramebuffer));
+
+    memset(&framebuffer_create_info, 0, sizeof(VkFramebufferCreateInfo));
+    framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+    framebuffer_create_info.renderPass = dev->render_pass;
+    framebuffer_create_info.attachmentCount = 1;
+    framebuffer_create_info.width = framebuffer_width;
+    framebuffer_create_info.height = framebuffer_height;
+    framebuffer_create_info.layers = 1;
+    for (i = 0; i < dev->image_views_len; i++) {
+        framebuffer_create_info.pAttachments = &dev->image_views[i];
+        result =
+            vkCreateFramebuffer(dev->logical_device, &framebuffer_create_info,
+                                NULL, &dev->framebuffers[i]);
+        NK_ASSERT(result == VK_SUCCESS);
+    }
+    dev->framebuffers_len = dev->image_views_len;
+}
+
+NK_INTERN void nk_glfw3_create_descriptor_pool(struct nk_glfw_device *dev) {
+    VkDescriptorPoolSize pool_sizes[2];
+    VkDescriptorPoolCreateInfo pool_info;
+    VkResult result;
+
+    memset(&pool_sizes, 0, sizeof(VkDescriptorPoolSize) * 2);
+    pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    pool_sizes[0].descriptorCount = 1;
+    pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    pool_sizes[1].descriptorCount = NK_GLFW_MAX_TEXTURES;
+
+    memset(&pool_info, 0, sizeof(VkDescriptorPoolCreateInfo));
+    pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+    pool_info.poolSizeCount = 2;
+    pool_info.pPoolSizes = pool_sizes;
+    pool_info.maxSets = 1 + NK_GLFW_MAX_TEXTURES;
+
+    result = vkCreateDescriptorPool(dev->logical_device, &pool_info, NULL,
+                                    &dev->descriptor_pool);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void
+nk_glfw3_create_uniform_descriptor_set_layout(struct nk_glfw_device *dev) {
+    VkDescriptorSetLayoutBinding binding;
+    VkDescriptorSetLayoutCreateInfo descriptor_set_info;
+    VkResult result;
+
+    memset(&binding, 0, sizeof(VkDescriptorSetLayoutBinding));
+    binding.binding = 0;
+    binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    binding.descriptorCount = 1;
+    binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+
+    memset(&descriptor_set_info, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
+    descriptor_set_info.sType =
+        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+    descriptor_set_info.bindingCount = 1;
+    descriptor_set_info.pBindings = &binding;
+
+    result =
+        vkCreateDescriptorSetLayout(dev->logical_device, &descriptor_set_info,
+                                    NULL, &dev->uniform_descriptor_set_layout);
+
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void
+nk_glfw3_create_and_update_uniform_descriptor_set(struct nk_glfw_device *dev) {
+    VkDescriptorSetAllocateInfo allocate_info;
+    VkDescriptorBufferInfo buffer_info;
+    VkWriteDescriptorSet descriptor_write;
+    VkResult result;
+
+    memset(&allocate_info, 0, sizeof(VkDescriptorSetAllocateInfo));
+    allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    allocate_info.descriptorPool = dev->descriptor_pool;
+    allocate_info.descriptorSetCount = 1;
+    allocate_info.pSetLayouts = &dev->uniform_descriptor_set_layout;
+
+    result = vkAllocateDescriptorSets(dev->logical_device, &allocate_info,
+                                      &dev->uniform_descriptor_set);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&buffer_info, 0, sizeof(VkDescriptorBufferInfo));
+    buffer_info.buffer = dev->uniform_buffer;
+    buffer_info.offset = 0;
+    buffer_info.range = sizeof(struct Mat4f);
+
+    memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet));
+    descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    descriptor_write.dstSet = dev->uniform_descriptor_set;
+    descriptor_write.dstBinding = 0;
+    descriptor_write.dstArrayElement = 0;
+    descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+    descriptor_write.descriptorCount = 1;
+    descriptor_write.pBufferInfo = &buffer_info;
+
+    vkUpdateDescriptorSets(dev->logical_device, 1, &descriptor_write, 0, NULL);
+}
+
+NK_INTERN void
+nk_glfw3_create_texture_descriptor_set_layout(struct nk_glfw_device *dev) {
+    VkDescriptorSetLayoutBinding binding;
+    VkDescriptorSetLayoutCreateInfo descriptor_set_info;
+    VkResult result;
+
+    memset(&binding, 0, sizeof(VkDescriptorSetLayoutBinding));
+    binding.binding = 0;
+    binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    binding.descriptorCount = 1;
+    binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+
+    memset(&descriptor_set_info, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
+    descriptor_set_info.sType =
+        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+    descriptor_set_info.bindingCount = 1;
+    descriptor_set_info.pBindings = &binding;
+
+    result =
+        vkCreateDescriptorSetLayout(dev->logical_device, &descriptor_set_info,
+                                    NULL, &dev->texture_descriptor_set_layout);
+
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void
+nk_glfw3_create_texture_descriptor_sets(struct nk_glfw_device *dev) {
+    VkDescriptorSetLayout *descriptor_set_layouts;
+    VkDescriptorSet *descriptor_sets;
+    VkDescriptorSetAllocateInfo allocate_info;
+    VkResult result;
+    int i;
+
+    descriptor_set_layouts = (VkDescriptorSetLayout *)malloc(
+        NK_GLFW_MAX_TEXTURES * sizeof(VkDescriptorSetLayout));
+    descriptor_sets = (VkDescriptorSet *)malloc(NK_GLFW_MAX_TEXTURES *
+                                                sizeof(VkDescriptorSet));
+
+    dev->texture_descriptor_sets =
+        (struct nk_vulkan_texture_descriptor_set *)malloc(
+            NK_GLFW_MAX_TEXTURES *
+            sizeof(struct nk_vulkan_texture_descriptor_set));
+    dev->texture_descriptor_sets_len = 0;
+
+    for (i = 0; i < NK_GLFW_MAX_TEXTURES; i++) {
+        descriptor_set_layouts[i] = dev->texture_descriptor_set_layout;
+        descriptor_sets[i] = dev->texture_descriptor_sets[i].descriptor_set;
+    }
+
+    memset(&allocate_info, 0, sizeof(VkDescriptorSetAllocateInfo));
+    allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    allocate_info.descriptorPool = dev->descriptor_pool;
+    allocate_info.descriptorSetCount = NK_GLFW_MAX_TEXTURES;
+    allocate_info.pSetLayouts = descriptor_set_layouts;
+
+    result = vkAllocateDescriptorSets(dev->logical_device, &allocate_info,
+                                      descriptor_sets);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    for (i = 0; i < NK_GLFW_MAX_TEXTURES; i++) {
+        dev->texture_descriptor_sets[i].descriptor_set = descriptor_sets[i];
+    }
+    free(descriptor_set_layouts);
+    free(descriptor_sets);
+}
+
+NK_INTERN void nk_glfw3_create_pipeline_layout(struct nk_glfw_device *dev) {
+    VkPipelineLayoutCreateInfo pipeline_layout_info;
+    VkDescriptorSetLayout descriptor_set_layouts[2];
+    VkResult result;
+
+    descriptor_set_layouts[0] = dev->uniform_descriptor_set_layout;
+    descriptor_set_layouts[1] = dev->texture_descriptor_set_layout;
+
+    memset(&pipeline_layout_info, 0, sizeof(VkPipelineLayoutCreateInfo));
+    pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+    pipeline_layout_info.setLayoutCount = 2;
+    pipeline_layout_info.pSetLayouts = descriptor_set_layouts;
+
+    result = (vkCreatePipelineLayout(dev->logical_device, &pipeline_layout_info,
+                                     NULL, &dev->pipeline_layout));
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN VkPipelineShaderStageCreateInfo
+nk_glfw3_create_shader(struct nk_glfw_device *dev, unsigned char *spv_shader,
+                       uint32_t size, VkShaderStageFlagBits stage_bit) {
+    VkShaderModuleCreateInfo create_info;
+    VkPipelineShaderStageCreateInfo shader_info;
+    VkShaderModule module = NULL;
+    VkResult result;
+
+    memset(&create_info, 0, sizeof(VkShaderModuleCreateInfo));
+    create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+    create_info.codeSize = size;
+    create_info.pCode = (const uint32_t *)spv_shader;
+    result =
+        vkCreateShaderModule(dev->logical_device, &create_info, NULL, &module);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&shader_info, 0, sizeof(VkPipelineShaderStageCreateInfo));
+    shader_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+    shader_info.stage = stage_bit;
+    shader_info.module = module;
+    shader_info.pName = "main";
+    return shader_info;
+}
+
+NK_INTERN void nk_glfw3_create_pipeline(struct nk_glfw_device *dev) {
+    VkPipelineInputAssemblyStateCreateInfo input_assembly_state;
+    VkPipelineRasterizationStateCreateInfo rasterization_state;
+    VkPipelineColorBlendAttachmentState attachment_state = {
+        VK_TRUE,
+        VK_BLEND_FACTOR_SRC_ALPHA,
+        VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+        VK_BLEND_OP_ADD,
+        VK_BLEND_FACTOR_SRC_ALPHA,
+        VK_BLEND_FACTOR_ONE,
+        VK_BLEND_OP_ADD,
+        VK_COLOR_COMPONENT_MASK_RGBA,
+    };
+    VkPipelineColorBlendStateCreateInfo color_blend_state;
+    VkPipelineViewportStateCreateInfo viewport_state;
+    VkPipelineMultisampleStateCreateInfo multisample_state;
+    VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT,
+                                        VK_DYNAMIC_STATE_SCISSOR};
+    VkPipelineDynamicStateCreateInfo dynamic_state;
+    VkPipelineShaderStageCreateInfo shader_stages[2];
+    VkVertexInputBindingDescription vertex_input_info;
+    VkVertexInputAttributeDescription vertex_attribute_description[3];
+    VkPipelineVertexInputStateCreateInfo vertex_input;
+    VkGraphicsPipelineCreateInfo pipeline_info;
+    VkResult result;
+
+    memset(&input_assembly_state, 0,
+           sizeof(VkPipelineInputAssemblyStateCreateInfo));
+    input_assembly_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+    input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+    input_assembly_state.primitiveRestartEnable = VK_FALSE;
+
+    memset(&rasterization_state, 0,
+           sizeof(VkPipelineRasterizationStateCreateInfo));
+    rasterization_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+    rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
+    rasterization_state.cullMode = VK_CULL_MODE_NONE;
+    rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+    rasterization_state.lineWidth = 1.0f;
+
+    memset(&color_blend_state, 0, sizeof(VkPipelineColorBlendStateCreateInfo));
+    color_blend_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+    color_blend_state.attachmentCount = 1;
+    color_blend_state.pAttachments = &attachment_state;
+
+    memset(&viewport_state, 0, sizeof(VkPipelineViewportStateCreateInfo));
+    viewport_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+    viewport_state.viewportCount = 1;
+    viewport_state.scissorCount = 1;
+
+    memset(&multisample_state, 0, sizeof(VkPipelineMultisampleStateCreateInfo));
+    multisample_state.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+    multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+    memset(&dynamic_state, 0, sizeof(VkPipelineDynamicStateCreateInfo));
+    dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+    dynamic_state.pDynamicStates = dynamic_states;
+    dynamic_state.dynamicStateCount = 2;
+
+    shader_stages[0] = nk_glfw3_create_shader(
+        dev, nuklearshaders_nuklear_vert_spv,
+        nuklearshaders_nuklear_vert_spv_len, VK_SHADER_STAGE_VERTEX_BIT);
+    shader_stages[1] = nk_glfw3_create_shader(
+        dev, nuklearshaders_nuklear_frag_spv,
+        nuklearshaders_nuklear_frag_spv_len, VK_SHADER_STAGE_FRAGMENT_BIT);
+
+    memset(&vertex_input_info, 0, sizeof(VkVertexInputBindingDescription));
+    vertex_input_info.binding = 0;
+    vertex_input_info.stride = sizeof(struct nk_glfw_vertex);
+    vertex_input_info.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+    memset(&vertex_attribute_description, 0,
+           sizeof(VkVertexInputAttributeDescription) * 3);
+    vertex_attribute_description[0].location = 0;
+    vertex_attribute_description[0].format = VK_FORMAT_R32G32_SFLOAT;
+    vertex_attribute_description[0].offset =
+        NK_OFFSETOF(struct nk_glfw_vertex, position);
+    vertex_attribute_description[1].location = 1;
+    vertex_attribute_description[1].format = VK_FORMAT_R32G32_SFLOAT;
+    vertex_attribute_description[1].offset =
+        NK_OFFSETOF(struct nk_glfw_vertex, uv);
+    vertex_attribute_description[2].location = 2;
+    vertex_attribute_description[2].format = VK_FORMAT_R8G8B8A8_UINT;
+    vertex_attribute_description[2].offset =
+        NK_OFFSETOF(struct nk_glfw_vertex, col);
+
+    memset(&vertex_input, 0, sizeof(VkPipelineVertexInputStateCreateInfo));
+    vertex_input.sType =
+        VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+    vertex_input.vertexBindingDescriptionCount = 1;
+    vertex_input.pVertexBindingDescriptions = &vertex_input_info;
+    vertex_input.vertexAttributeDescriptionCount = 3;
+    vertex_input.pVertexAttributeDescriptions = vertex_attribute_description;
+
+    memset(&pipeline_info, 0, sizeof(VkGraphicsPipelineCreateInfo));
+    pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+    pipeline_info.flags = 0;
+    pipeline_info.stageCount = 2;
+    pipeline_info.pStages = shader_stages;
+    pipeline_info.pVertexInputState = &vertex_input;
+    pipeline_info.pInputAssemblyState = &input_assembly_state;
+    pipeline_info.pViewportState = &viewport_state;
+    pipeline_info.pRasterizationState = &rasterization_state;
+    pipeline_info.pMultisampleState = &multisample_state;
+    pipeline_info.pColorBlendState = &color_blend_state;
+    pipeline_info.pDynamicState = &dynamic_state;
+    pipeline_info.layout = dev->pipeline_layout;
+    pipeline_info.renderPass = dev->render_pass;
+    pipeline_info.basePipelineIndex = -1;
+    pipeline_info.basePipelineHandle = NULL;
+
+    result = vkCreateGraphicsPipelines(dev->logical_device, NULL, 1,
+                                       &pipeline_info, NULL, &dev->pipeline);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    vkDestroyShaderModule(dev->logical_device, shader_stages[0].module, NULL);
+    vkDestroyShaderModule(dev->logical_device, shader_stages[1].module, NULL);
+}
+
+NK_INTERN void nk_glfw3_create_render_resources(struct nk_glfw_device *dev,
+                                                uint32_t framebuffer_width,
+                                                uint32_t framebuffer_height) {
+    nk_glfw3_create_render_pass(dev);
+    nk_glfw3_create_framebuffers(dev, framebuffer_width, framebuffer_height);
+    nk_glfw3_create_descriptor_pool(dev);
+    nk_glfw3_create_uniform_descriptor_set_layout(dev);
+    nk_glfw3_create_and_update_uniform_descriptor_set(dev);
+    nk_glfw3_create_texture_descriptor_set_layout(dev);
+    nk_glfw3_create_texture_descriptor_sets(dev);
+    nk_glfw3_create_pipeline_layout(dev);
+    nk_glfw3_create_pipeline(dev);
+}
+
+NK_API void nk_glfw3_device_create(
+    VkDevice logical_device, VkPhysicalDevice physical_device,
+    uint32_t graphics_queue_family_index, VkImageView *image_views,
+    uint32_t image_views_len, VkFormat color_format,
+    VkDeviceSize max_vertex_buffer, VkDeviceSize max_element_buffer,
+    uint32_t framebuffer_width, uint32_t framebuffer_height) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+    dev->max_vertex_buffer = max_vertex_buffer;
+    dev->max_element_buffer = max_element_buffer;
+    nk_buffer_init_default(&dev->cmds);
+    dev->logical_device = logical_device;
+    dev->physical_device = physical_device;
+    dev->image_views = image_views;
+    dev->image_views_len = image_views_len;
+    dev->color_format = color_format;
+    dev->framebuffers = NULL;
+    dev->framebuffers_len = 0;
+
+    nk_glfw3_create_sampler(dev);
+    nk_glfw3_create_command_pool(dev, graphics_queue_family_index);
+    nk_glfw3_create_command_buffers(dev);
+    nk_glfw3_create_semaphore(dev);
+
+    nk_glfw3_create_buffer_and_memory(dev, &dev->vertex_buffer,
+                                      VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+                                      &dev->vertex_memory, max_vertex_buffer);
+    nk_glfw3_create_buffer_and_memory(dev, &dev->index_buffer,
+                                      VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+                                      &dev->index_memory, max_element_buffer);
+    nk_glfw3_create_buffer_and_memory(
+        dev, &dev->uniform_buffer, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+        &dev->uniform_memory, sizeof(struct Mat4f));
+
+    vkMapMemory(dev->logical_device, dev->vertex_memory, 0, max_vertex_buffer,
+                0, &dev->mapped_vertex);
+    vkMapMemory(dev->logical_device, dev->index_memory, 0, max_element_buffer,
+                0, &dev->mapped_index);
+    vkMapMemory(dev->logical_device, dev->uniform_memory, 0,
+                sizeof(struct Mat4f), 0, &dev->mapped_uniform);
+
+    nk_glfw3_create_render_resources(dev, framebuffer_width,
+                                     framebuffer_height);
+}
+
+NK_INTERN void nk_glfw3_device_upload_atlas(VkQueue graphics_queue,
+                                            const void *image, int width,
+                                            int height) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+
+    VkImageCreateInfo image_info;
+    VkResult result;
+    VkMemoryRequirements mem_reqs;
+    VkMemoryAllocateInfo alloc_info;
+    VkBufferCreateInfo buffer_info;
+    uint8_t *data = 0;
+    VkCommandBufferBeginInfo begin_info;
+    VkCommandBuffer command_buffer;
+    VkImageMemoryBarrier image_memory_barrier;
+    VkBufferImageCopy buffer_copy_region;
+    VkImageMemoryBarrier image_shader_memory_barrier;
+    VkFence fence;
+    VkFenceCreateInfo fence_create;
+    VkSubmitInfo submit_info;
+    VkImageViewCreateInfo image_view_info;
+    struct {
+        VkDeviceMemory memory;
+        VkBuffer buffer;
+    } staging_buffer;
+
+    memset(&image_info, 0, sizeof(VkImageCreateInfo));
+    image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    image_info.imageType = VK_IMAGE_TYPE_2D;
+    image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
+    image_info.extent.width = (uint32_t)width;
+    image_info.extent.height = (uint32_t)height;
+    image_info.extent.depth = 1;
+    image_info.mipLevels = 1;
+    image_info.arrayLayers = 1;
+    image_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+    image_info.usage =
+        VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+    image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    result =
+        vkCreateImage(dev->logical_device, &image_info, NULL, &dev->font_image);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    vkGetImageMemoryRequirements(dev->logical_device, dev->font_image,
+                                 &mem_reqs);
+
+    memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo));
+    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+    alloc_info.allocationSize = mem_reqs.size;
+    alloc_info.memoryTypeIndex = nk_glfw3_find_memory_index(
+        dev->physical_device, mem_reqs.memoryTypeBits,
+        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+    result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL,
+                              &dev->font_memory);
+    NK_ASSERT(result == VK_SUCCESS);
+    result = vkBindImageMemory(dev->logical_device, dev->font_image,
+                               dev->font_memory, 0);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&buffer_info, 0, sizeof(VkBufferCreateInfo));
+    buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+    buffer_info.size = alloc_info.allocationSize;
+    buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+    buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+    result = vkCreateBuffer(dev->logical_device, &buffer_info, NULL,
+                            &staging_buffer.buffer);
+    NK_ASSERT(result == VK_SUCCESS);
+    vkGetBufferMemoryRequirements(dev->logical_device, staging_buffer.buffer,
+                                  &mem_reqs);
+
+    alloc_info.allocationSize = mem_reqs.size;
+    alloc_info.memoryTypeIndex = nk_glfw3_find_memory_index(
+        dev->physical_device, mem_reqs.memoryTypeBits,
+        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+            VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+    result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL,
+                              &staging_buffer.memory);
+    NK_ASSERT(result == VK_SUCCESS);
+    result = vkBindBufferMemory(dev->logical_device, staging_buffer.buffer,
+                                staging_buffer.memory, 0);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    result = vkMapMemory(dev->logical_device, staging_buffer.memory, 0,
+                         alloc_info.allocationSize, 0, (void **)&data);
+    NK_ASSERT(result == VK_SUCCESS);
+    memcpy(data, image, width * height * 4);
+    vkUnmapMemory(dev->logical_device, staging_buffer.memory);
+
+    memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo));
+    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+    NK_ASSERT(dev->command_buffers_len > 0);
+    /*
+    use the same command buffer as for render as we are regenerating the
+    buffer during render anyway
+    */
+    command_buffer = dev->command_buffers[0];
+    result = vkBeginCommandBuffer(command_buffer, &begin_info);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&image_memory_barrier, 0, sizeof(VkImageMemoryBarrier));
+    image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    image_memory_barrier.image = dev->font_image;
+    image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+    image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+    image_memory_barrier.subresourceRange.aspectMask =
+        VK_IMAGE_ASPECT_COLOR_BIT;
+    image_memory_barrier.subresourceRange.levelCount = 1;
+    image_memory_barrier.subresourceRange.layerCount = 1;
+    image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+                         VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
+                         &image_memory_barrier);
+
+    memset(&buffer_copy_region, 0, sizeof(VkBufferImageCopy));
+    buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    buffer_copy_region.imageSubresource.layerCount = 1;
+    buffer_copy_region.imageExtent.width = (uint32_t)width;
+    buffer_copy_region.imageExtent.height = (uint32_t)height;
+    buffer_copy_region.imageExtent.depth = 1;
+
+    vkCmdCopyBufferToImage(
+        command_buffer, staging_buffer.buffer, dev->font_image,
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_copy_region);
+
+    memset(&image_shader_memory_barrier, 0, sizeof(VkImageMemoryBarrier));
+    image_shader_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    image_shader_memory_barrier.image = dev->font_image;
+    image_shader_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_shader_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    image_shader_memory_barrier.oldLayout =
+        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+    image_shader_memory_barrier.newLayout =
+        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+    image_shader_memory_barrier.subresourceRange.aspectMask =
+        VK_IMAGE_ASPECT_COLOR_BIT;
+    image_shader_memory_barrier.subresourceRange.levelCount = 1;
+    image_shader_memory_barrier.subresourceRange.layerCount = 1;
+    image_shader_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+    image_shader_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
+                         VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0,
+                         NULL, 1, &image_shader_memory_barrier);
+
+    result = vkEndCommandBuffer(command_buffer);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&fence_create, 0, sizeof(VkFenceCreateInfo));
+    fence_create.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+
+    result = vkCreateFence(dev->logical_device, &fence_create, NULL, &fence);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    memset(&submit_info, 0, sizeof(VkSubmitInfo));
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &command_buffer;
+
+    result = vkQueueSubmit(graphics_queue, 1, &submit_info, fence);
+    NK_ASSERT(result == VK_SUCCESS);
+    result =
+        vkWaitForFences(dev->logical_device, 1, &fence, VK_TRUE, UINT64_MAX);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    vkDestroyFence(dev->logical_device, fence, NULL);
+
+    vkFreeMemory(dev->logical_device, staging_buffer.memory, NULL);
+    vkDestroyBuffer(dev->logical_device, staging_buffer.buffer, NULL);
+
+    memset(&image_view_info, 0, sizeof(VkImageViewCreateInfo));
+    image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    image_view_info.image = dev->font_image;
+    image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    image_view_info.format = image_info.format;
+    image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    image_view_info.subresourceRange.layerCount = 1;
+    image_view_info.subresourceRange.levelCount = 1;
+
+    result = vkCreateImageView(dev->logical_device, &image_view_info, NULL,
+                               &dev->font_image_view);
+    NK_ASSERT(result == VK_SUCCESS);
+}
+
+NK_INTERN void nk_glfw3_destroy_render_resources(struct nk_glfw_device *dev) {
+    uint32_t i;
+
+    vkDestroyPipeline(dev->logical_device, dev->pipeline, NULL);
+    vkDestroyPipelineLayout(dev->logical_device, dev->pipeline_layout, NULL);
+    vkDestroyDescriptorSetLayout(dev->logical_device,
+                                 dev->texture_descriptor_set_layout, NULL);
+    vkDestroyDescriptorSetLayout(dev->logical_device,
+                                 dev->uniform_descriptor_set_layout, NULL);
+    vkDestroyDescriptorPool(dev->logical_device, dev->descriptor_pool, NULL);
+    for (i = 0; i < dev->framebuffers_len; i++) {
+        vkDestroyFramebuffer(dev->logical_device, dev->framebuffers[i], NULL);
+    }
+    free(dev->framebuffers);
+    dev->framebuffers_len = 0;
+    free(dev->texture_descriptor_sets);
+    dev->texture_descriptor_sets_len = 0;
+    vkDestroyRenderPass(dev->logical_device, dev->render_pass, NULL);
+}
+
+NK_API void nk_glfw3_resize(uint32_t framebuffer_width,
+                            uint32_t framebuffer_height) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+    glfwGetWindowSize(glfw.win, &glfw.width, &glfw.height);
+    glfwGetFramebufferSize(glfw.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_glfw3_destroy_render_resources(dev);
+    nk_glfw3_create_render_resources(dev, framebuffer_width,
+                                     framebuffer_height);
+}
+
+NK_API void nk_glfw3_device_destroy(void) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+
+    vkDeviceWaitIdle(dev->logical_device);
+
+    nk_glfw3_destroy_render_resources(dev);
+
+    vkFreeCommandBuffers(dev->logical_device, dev->command_pool,
+                         dev->command_buffers_len, dev->command_buffers);
+    vkDestroyCommandPool(dev->logical_device, dev->command_pool, NULL);
+    vkDestroySemaphore(dev->logical_device, dev->render_completed, NULL);
+
+    vkUnmapMemory(dev->logical_device, dev->vertex_memory);
+    vkUnmapMemory(dev->logical_device, dev->index_memory);
+    vkUnmapMemory(dev->logical_device, dev->uniform_memory);
+
+    vkFreeMemory(dev->logical_device, dev->vertex_memory, NULL);
+    vkFreeMemory(dev->logical_device, dev->index_memory, NULL);
+    vkFreeMemory(dev->logical_device, dev->uniform_memory, NULL);
+
+    vkDestroyBuffer(dev->logical_device, dev->vertex_buffer, NULL);
+    vkDestroyBuffer(dev->logical_device, dev->index_buffer, NULL);
+    vkDestroyBuffer(dev->logical_device, dev->uniform_buffer, NULL);
+
+    vkDestroySampler(dev->logical_device, dev->sampler, NULL);
+
+    vkFreeMemory(dev->logical_device, dev->font_memory, NULL);
+    vkDestroyImage(dev->logical_device, dev->font_image, NULL);
+    vkDestroyImageView(dev->logical_device, dev->font_image_view, NULL);
+
+    free(dev->command_buffers);
+    nk_buffer_free(&dev->cmds);
+}
+
+NK_API
+void nk_glfw3_shutdown(void) {
+    nk_font_atlas_clear(&glfw.atlas);
+    nk_free(&glfw.ctx);
+    nk_glfw3_device_destroy();
+    memset(&glfw, 0, sizeof(glfw));
+}
+
+NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas) {
+    nk_font_atlas_init_default(&glfw.atlas);
+    nk_font_atlas_begin(&glfw.atlas);
+    *atlas = &glfw.atlas;
+}
+
+NK_API void nk_glfw3_font_stash_end(VkQueue graphics_queue) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+
+    const void *image;
+    int w, h;
+    image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
+    nk_glfw3_device_upload_atlas(graphics_queue, image, w, h);
+    nk_font_atlas_end(&glfw.atlas, nk_handle_ptr(dev->font_image_view),
+                      &dev->tex_null);
+    if (glfw.atlas.default_font) {
+        nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle);
+    }
+}
+
+NK_API void nk_glfw3_new_frame(void) {
+    int i;
+    double x, y;
+    struct nk_context *ctx = &glfw.ctx;
+    struct GLFWwindow *win = glfw.win;
+
+    nk_input_begin(ctx);
+    for (i = 0; i < glfw.text_len; ++i)
+        nk_input_unicode(ctx, glfw.text[i]);
+
+#ifdef NK_GLFW_GL4_MOUSE_GRABBING
+    /* optional grabbing behavior */
+    if (ctx->input.mouse.grab)
+        glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+    else if (ctx->input.mouse.ungrab)
+        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);
+    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);
+    } 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);
+        nk_input_key(ctx, NK_KEY_COPY, 0);
+        nk_input_key(ctx, NK_KEY_PASTE, 0);
+        nk_input_key(ctx, NK_KEY_CUT, 0);
+        nk_input_key(ctx, NK_KEY_SHIFT, 0);
+    }
+
+    glfwGetCursorPos(win, &x, &y);
+    nk_input_motion(ctx, (int)x, (int)y);
+#ifdef NK_GLFW_GL4_MOUSE_GRABBING
+    if (ctx->input.mouse.grabbed) {
+        glfwSetCursorPos(glfw.win, ctx->input.mouse.prev.x,
+                         ctx->input.mouse.prev.y);
+        ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
+        ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
+    }
+#endif
+    nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y,
+                    glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) ==
+                        GLFW_PRESS);
+    nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y,
+                    glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) ==
+                        GLFW_PRESS);
+    nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y,
+                    glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) ==
+                        GLFW_PRESS);
+    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);
+    glfw.text_len = 0;
+    glfw.scroll = nk_vec2(0, 0);
+}
+
+NK_INTERN void update_texture_descriptor_set(
+    struct nk_glfw_device *dev,
+    struct nk_vulkan_texture_descriptor_set *texture_descriptor_set,
+    VkImageView image_view) {
+    VkDescriptorImageInfo descriptor_image_info;
+    VkWriteDescriptorSet descriptor_write;
+
+    texture_descriptor_set->image_view = image_view;
+
+    memset(&descriptor_image_info, 0, sizeof(VkDescriptorImageInfo));
+    descriptor_image_info.sampler = dev->sampler;
+    descriptor_image_info.imageView = texture_descriptor_set->image_view;
+    descriptor_image_info.imageLayout =
+        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+    memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet));
+    descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    descriptor_write.dstSet = texture_descriptor_set->descriptor_set;
+    descriptor_write.dstBinding = 0;
+    descriptor_write.dstArrayElement = 0;
+    descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    descriptor_write.descriptorCount = 1;
+    descriptor_write.pImageInfo = &descriptor_image_info;
+
+    vkUpdateDescriptorSets(dev->logical_device, 1, &descriptor_write, 0, NULL);
+}
+
+NK_API
+VkSemaphore nk_glfw3_render(VkQueue graphics_queue, uint32_t buffer_index,
+                            VkSemaphore wait_semaphore,
+                            enum nk_anti_aliasing AA) {
+    struct nk_glfw_device *dev = &glfw.vulkan;
+    struct nk_buffer vbuf, ebuf;
+
+    struct Mat4f projection = {
+        {2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f,
+         0.0f, -1.0f, 1.0f, 0.0f, 1.0f},
+    };
+
+    VkCommandBufferBeginInfo begin_info;
+    VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 0.0f}}};
+    VkRenderPassBeginInfo render_pass_begin_nfo;
+    VkCommandBuffer command_buffer;
+    VkResult result;
+    VkViewport viewport;
+
+    VkDeviceSize doffset = 0;
+    VkImageView current_texture = NULL;
+    uint32_t index_offset = 0;
+    VkRect2D scissor;
+    uint32_t wait_semaphore_count;
+    VkSemaphore *wait_semaphores;
+    VkPipelineStageFlags wait_stage =
+        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    VkSubmitInfo submit_info;
+
+    projection.m[0] /= glfw.width;
+    projection.m[5] /= glfw.height;
+
+    memcpy(dev->mapped_uniform, &projection, sizeof(projection));
+
+    memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo));
+    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+    memset(&render_pass_begin_nfo, 0, sizeof(VkRenderPassBeginInfo));
+    render_pass_begin_nfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+    render_pass_begin_nfo.renderPass = dev->render_pass;
+    render_pass_begin_nfo.renderArea.extent.width = (uint32_t)glfw.width;
+    render_pass_begin_nfo.renderArea.extent.height = (uint32_t)glfw.height;
+    render_pass_begin_nfo.clearValueCount = 1;
+    render_pass_begin_nfo.pClearValues = &clear_value;
+    render_pass_begin_nfo.framebuffer = dev->framebuffers[buffer_index];
+
+    command_buffer = dev->command_buffers[buffer_index];
+
+    result = vkBeginCommandBuffer(command_buffer, &begin_info);
+    NK_ASSERT(result == VK_SUCCESS);
+    vkCmdBeginRenderPass(command_buffer, &render_pass_begin_nfo,
+                         VK_SUBPASS_CONTENTS_INLINE);
+
+    memset(&viewport, 0, sizeof(VkViewport));
+    viewport.width = (float)glfw.width;
+    viewport.height = (float)glfw.height;
+    viewport.maxDepth = 1.0f;
+    vkCmdSetViewport(command_buffer, 0, 1, &viewport);
+
+    vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                      dev->pipeline);
+    vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                            dev->pipeline_layout, 0, 1,
+                            &dev->uniform_descriptor_set, 0, NULL);
+    {
+        /* convert from command queue into draw list and draw to screen */
+        const struct nk_draw_command *cmd;
+        /* load draw vertices & elements directly into vertex + element buffer
+         */
+        {
+            /* fill convert configuration */
+            struct nk_convert_config config;
+            static const struct nk_draw_vertex_layout_element vertex_layout[] =
+                {{NK_VERTEX_POSITION, NK_FORMAT_FLOAT,
+                  NK_OFFSETOF(struct nk_glfw_vertex, position)},
+                 {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT,
+                  NK_OFFSETOF(struct nk_glfw_vertex, uv)},
+                 {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8,
+                  NK_OFFSETOF(struct nk_glfw_vertex, col)},
+                 {NK_VERTEX_LAYOUT_END}};
+            NK_MEMSET(&config, 0, sizeof(config));
+            config.vertex_layout = vertex_layout;
+            config.vertex_size = sizeof(struct nk_glfw_vertex);
+            config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex);
+            config.tex_null = dev->tex_null;
+            config.circle_segment_count = 22;
+            config.curve_segment_count = 22;
+            config.arc_segment_count = 22;
+            config.global_alpha = 1.0f;
+            config.shape_AA = AA;
+            config.line_AA = AA;
+
+            /* setup buffers to load vertices and elements */
+            nk_buffer_init_fixed(&vbuf, dev->mapped_vertex,
+                                 (size_t)dev->max_vertex_buffer);
+            nk_buffer_init_fixed(&ebuf, dev->mapped_index,
+                                 (size_t)dev->max_element_buffer);
+            nk_convert(&glfw.ctx, &dev->cmds, &vbuf, &ebuf, &config);
+        }
+
+        /* iterate over and execute each draw command */
+
+        vkCmdBindVertexBuffers(command_buffer, 0, 1, &dev->vertex_buffer,
+                               &doffset);
+        vkCmdBindIndexBuffer(command_buffer, dev->index_buffer, 0,
+                             VK_INDEX_TYPE_UINT16);
+
+        nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds) {
+            if (!cmd->texture.ptr) {
+                continue;
+            }
+            if (cmd->texture.ptr && cmd->texture.ptr != current_texture) {
+                int found = 0;
+                uint32_t i;
+                for (i = 0; i < dev->texture_descriptor_sets_len; i++) {
+                    if (dev->texture_descriptor_sets[i].image_view ==
+                        cmd->texture.ptr) {
+                        found = 1;
+                        break;
+                    }
+                }
+
+                if (!found) {
+                    update_texture_descriptor_set(
+                        dev, &dev->texture_descriptor_sets[i],
+                        (VkImageView)cmd->texture.ptr);
+                    dev->texture_descriptor_sets_len++;
+                }
+                vkCmdBindDescriptorSets(
+                    command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+                    dev->pipeline_layout, 1, 1,
+                    &dev->texture_descriptor_sets[i].descriptor_set, 0, NULL);
+            }
+
+            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);
+            vkCmdSetScissor(command_buffer, 0, 1, &scissor);
+            vkCmdDrawIndexed(command_buffer, cmd->elem_count, 1, index_offset,
+                             0, 0);
+            index_offset += cmd->elem_count;
+        }
+        nk_clear(&glfw.ctx);
+    }
+
+    vkCmdEndRenderPass(command_buffer);
+    result = vkEndCommandBuffer(command_buffer);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    if (wait_semaphore) {
+        wait_semaphore_count = 1;
+        wait_semaphores = &wait_semaphore;
+    } else {
+        wait_semaphore_count = 0;
+        wait_semaphores = NULL;
+    }
+
+    memset(&submit_info, 0, sizeof(VkSubmitInfo));
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &command_buffer;
+    submit_info.pWaitDstStageMask = &wait_stage;
+    submit_info.waitSemaphoreCount = wait_semaphore_count;
+    submit_info.pWaitSemaphores = wait_semaphores;
+    submit_info.signalSemaphoreCount = 1;
+    submit_info.pSignalSemaphores = &dev->render_completed;
+
+    result = vkQueueSubmit(graphics_queue, 1, &submit_info, NULL);
+    NK_ASSERT(result == VK_SUCCESS);
+
+    return dev->render_completed;
+}
+
+NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) {
+    (void)win;
+    if (glfw.text_len < NK_GLFW_TEXT_MAX)
+        glfw.text[glfw.text_len++] = codepoint;
+}
+
+NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff,
+                                     double yoff) {
+    (void)win;
+    (void)xoff;
+    glfw.scroll.x += (float)xoff;
+    glfw.scroll.y += (float)yoff;
+}
+
+NK_API void nk_glfw3_mouse_button_callback(GLFWwindow *window, int button,
+                                           int action, int mods) {
+    double x, y;
+    NK_UNUSED(mods);
+    if (button != GLFW_MOUSE_BUTTON_LEFT)
+        return;
+    glfwGetCursorPos(window, &x, &y);
+    if (action == GLFW_PRESS) {
+        double dt = glfwGetTime() - glfw.last_button_click;
+        if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) {
+            glfw.is_double_click_down = nk_true;
+            glfw.double_click_pos = nk_vec2((float)x, (float)y);
+        }
+        glfw.last_button_click = glfwGetTime();
+    } else
+        glfw.is_double_click_down = nk_false;
+}
+
+NK_INTERN void nk_glfw3_clipboard_paste(nk_handle usr,
+                                        struct nk_text_edit *edit) {
+    const char *text = glfwGetClipboardString(glfw.win);
+    if (text)
+        nk_textedit_paste(edit, text, nk_strlen(text));
+    (void)usr;
+}
+
+NK_INTERN void nk_glfw3_clipboard_copy(nk_handle usr, const char *text,
+                                       int len) {
+    char *str = 0;
+    (void)usr;
+    if (!len)
+        return;
+    str = (char *)malloc((size_t)len + 1);
+    if (!str)
+        return;
+    memcpy(str, text, (size_t)len);
+    str[len] = '\0';
+    glfwSetClipboardString(glfw.win, str);
+    free(str);
+}
+
+NK_API struct nk_context *
+nk_glfw3_init(GLFWwindow *win, VkDevice logical_device,
+              VkPhysicalDevice physical_device,
+              uint32_t graphics_queue_family_index, VkImageView *image_views,
+              uint32_t image_views_len, VkFormat color_format,
+              enum nk_glfw_init_state init_state,
+              VkDeviceSize max_vertex_buffer, VkDeviceSize max_element_buffer) {
+    memset(&glfw, 0, sizeof(struct nk_glfw));
+    glfw.win = win;
+    if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
+        glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
+        glfwSetCharCallback(win, nk_glfw3_char_callback);
+        glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
+    }
+    nk_init_default(&glfw.ctx, 0);
+    glfw.ctx.clip.copy = nk_glfw3_clipboard_copy;
+    glfw.ctx.clip.paste = nk_glfw3_clipboard_paste;
+    glfw.ctx.clip.userdata = nk_handle_ptr(0);
+    glfw.last_button_click = 0;
+
+    glfwGetWindowSize(win, &glfw.width, &glfw.height);
+    glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height);
+
+    nk_glfw3_device_create(logical_device, physical_device,
+                           graphics_queue_family_index, image_views,
+                           image_views_len, color_format, max_vertex_buffer,
+                           max_element_buffer, (uint32_t)glfw.display_width,
+                           (uint32_t)glfw.display_height);
+
+    glfw.is_double_click_down = nk_false;
+    glfw.double_click_pos = nk_vec2(0, 0);
+
+    return &glfw.ctx;
+}
+
+#endif

+ 13 - 0
demo/glfw_vulkan/src/nuklearshaders/nuklear.frag

@@ -0,0 +1,13 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(binding = 0, set = 1) uniform sampler2D currentTexture;
+
+layout(location = 0) in vec4 fragColor;
+layout(location = 1) in vec2 fragUv;
+layout(location = 0) out vec4 outColor;
+
+void main() {
+    vec4 texColor = texture(currentTexture, fragUv);
+    outColor = fragColor * texColor;
+}

+ 23 - 0
demo/glfw_vulkan/src/nuklearshaders/nuklear.vert

@@ -0,0 +1,23 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+out gl_PerVertex {
+    vec4 gl_Position;
+};
+
+layout(binding = 0) uniform UniformBufferObject {
+    mat4 projection;
+} ubo;
+
+layout(location = 0) in vec2 position;
+layout(location = 1) in vec2 uv;
+layout(location = 2) in uvec4 color;
+layout(location = 0) out vec4 fragColor;
+layout(location = 1) out vec2 fragUv;
+
+void main() {
+    gl_Position = ubo.projection * vec4(position, 0.0, 1.0);
+    gl_Position.y = -gl_Position.y;
+    fragColor = vec4(color[0]/255.0, color[1]/255.0, color[2]/255.0, color[3]/255.0);
+    fragUv = uv;
+}

+ 4 - 0
demo/sdl2surface_rawfb/sdl2surface_rawfb.h

@@ -49,6 +49,10 @@ void                  nk_sdlsurface_shutdown(struct sdlsurface_context *sdlsurfa
  * ===============================================================
  */
 #ifdef NK_SDLSURFACE_IMPLEMENTATION
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
 struct sdlsurface_context {
     struct nk_context ctx;
     struct nk_rect scissors;

+ 1 - 0
demo/sdl_opengl2/main.c

@@ -139,6 +139,7 @@ main(int argc, char *argv[])
             if (evt.type == SDL_QUIT) goto cleanup;
             nk_sdl_handle_event(&evt);
         }
+        nk_sdl_handle_grab(); /* optional grabbing behavior */
         nk_input_end(ctx);
 
         /* GUI */

+ 15 - 8
demo/sdl_opengl2/nuklear_sdl_gl2.h

@@ -30,6 +30,8 @@ NK_API void                 nk_sdl_shutdown(void);
  * ===============================================================
  */
 #ifdef NK_SDL_GL2_IMPLEMENTATION
+#include <string.h>
+#include <stdlib.h>
 
 struct nk_sdl_device {
     struct nk_buffer cmds;
@@ -231,21 +233,26 @@ nk_sdl_font_stash_end(void)
         nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle);
 }
 
-NK_API int
-nk_sdl_handle_event(SDL_Event *evt)
+NK_API void
+nk_sdl_handle_grab(void)
 {
     struct nk_context *ctx = &sdl.ctx;
-
-    /* optional grabbing behavior */
     if (ctx->input.mouse.grab) {
         SDL_SetRelativeMouseMode(SDL_TRUE);
-        ctx->input.mouse.grab = 0;
     } else if (ctx->input.mouse.ungrab) {
-        int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y;
+        /* better support for older SDL by setting mode first; causes an extra mouse motion event */
         SDL_SetRelativeMouseMode(SDL_FALSE);
-        SDL_WarpMouseInWindow(sdl.win, x, y);
-        ctx->input.mouse.ungrab = 0;
+        SDL_WarpMouseInWindow(sdl.win, (int)ctx->input.mouse.prev.x, (int)ctx->input.mouse.prev.y);
+    } else if (ctx->input.mouse.grabbed) {
+        ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
+        ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
     }
+}
+
+NK_API int
+nk_sdl_handle_event(SDL_Event *evt)
+{
+    struct nk_context *ctx = &sdl.ctx;
 
     switch(evt->type)
     {

+ 3 - 1
demo/sdl_opengl3/main.c

@@ -149,7 +149,9 @@ int main(int argc, char *argv[])
         while (SDL_PollEvent(&evt)) {
             if (evt.type == SDL_QUIT) goto cleanup;
             nk_sdl_handle_event(&evt);
-        } nk_input_end(ctx);
+        }
+        nk_sdl_handle_grab(); /* optional grabbing behavior */
+        nk_input_end(ctx);
 
         /* GUI */
         if (nk_begin(ctx, "Demo", nk_rect(50, 50, 230, 250),

+ 15 - 8
demo/sdl_opengl3/nuklear_sdl_gl3.h

@@ -36,6 +36,8 @@ NK_API void                 nk_sdl_device_create(void);
  */
 #ifdef NK_SDL_GL3_IMPLEMENTATION
 
+#include <stdlib.h>
+#include <assert.h>
 #include <string.h>
 
 struct nk_sdl_device {
@@ -340,21 +342,26 @@ nk_sdl_font_stash_end(void)
 
 }
 
-NK_API int
-nk_sdl_handle_event(SDL_Event *evt)
+NK_API void
+nk_sdl_handle_grab(void)
 {
     struct nk_context *ctx = &sdl.ctx;
-
-    /* optional grabbing behavior */
     if (ctx->input.mouse.grab) {
         SDL_SetRelativeMouseMode(SDL_TRUE);
-        ctx->input.mouse.grab = 0;
     } else if (ctx->input.mouse.ungrab) {
-        int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y;
+        /* better support for older SDL by setting mode first; causes an extra mouse motion event */
         SDL_SetRelativeMouseMode(SDL_FALSE);
-        SDL_WarpMouseInWindow(sdl.win, x, y);
-        ctx->input.mouse.ungrab = 0;
+        SDL_WarpMouseInWindow(sdl.win, (int)ctx->input.mouse.prev.x, (int)ctx->input.mouse.prev.y);
+    } else if (ctx->input.mouse.grabbed) {
+        ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
+        ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
     }
+}
+
+NK_API int
+nk_sdl_handle_event(SDL_Event *evt)
+{
+    struct nk_context *ctx = &sdl.ctx;
 
     switch(evt->type)
     {

+ 1 - 0
demo/sdl_opengles2/main.c

@@ -92,6 +92,7 @@ MainLoop(void* loopArg){
         if (evt.type == SDL_QUIT) running = nk_false;
         nk_sdl_handle_event(&evt);
     }
+    nk_sdl_handle_grab(); /* optional grabbing behavior */
     nk_input_end(ctx);
 
 

+ 15 - 9
demo/sdl_opengles2/nuklear_sdl_gles2.h

@@ -38,7 +38,8 @@ NK_API void                 nk_sdl_device_create(void);
  * ===============================================================
  */
 #ifdef NK_SDL_GLES2_IMPLEMENTATION
-
+#include <stdlib.h>
+#include <assert.h>
 #include <string.h>
 
 struct nk_sdl_device {
@@ -341,21 +342,26 @@ nk_sdl_font_stash_end(void)
 
 }
 
-NK_API int
-nk_sdl_handle_event(SDL_Event *evt)
+NK_API void
+nk_sdl_handle_grab(void)
 {
     struct nk_context *ctx = &sdl.ctx;
-
-    /* optional grabbing behavior */
     if (ctx->input.mouse.grab) {
         SDL_SetRelativeMouseMode(SDL_TRUE);
-        ctx->input.mouse.grab = 0;
     } else if (ctx->input.mouse.ungrab) {
-        int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y;
+        /* better support for older SDL by setting mode first; causes an extra mouse motion event */
         SDL_SetRelativeMouseMode(SDL_FALSE);
-        SDL_WarpMouseInWindow(sdl.win, x, y);
-        ctx->input.mouse.ungrab = 0;
+        SDL_WarpMouseInWindow(sdl.win, (int)ctx->input.mouse.prev.x, (int)ctx->input.mouse.prev.y);
+    } else if (ctx->input.mouse.grabbed) {
+        ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
+        ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
     }
+}
+
+NK_API int
+nk_sdl_handle_event(SDL_Event *evt)
+{
+    struct nk_context *ctx = &sdl.ctx;
 
     switch(evt->type)
     {

+ 1 - 0
demo/sdl_renderer/main.c

@@ -177,6 +177,7 @@ main(int argc, char *argv[])
             if (evt.type == SDL_QUIT) goto cleanup;
             nk_sdl_handle_event(&evt);
         }
+        nk_sdl_handle_grab(); /* optional grabbing behavior */
         nk_input_end(ctx);
 
         /* GUI */

+ 15 - 10
demo/sdl_renderer/nuklear_sdl_renderer.h

@@ -39,8 +39,8 @@ NK_API void                 nk_sdl_shutdown(void);
  * ===============================================================
  */
 #ifdef NK_SDL_RENDERER_IMPLEMENTATION
-
-#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
 
 struct nk_sdl_device {
     struct nk_buffer cmds;
@@ -264,21 +264,26 @@ nk_sdl_font_stash_end(void)
         nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle);
 }
 
-NK_API int
-nk_sdl_handle_event(SDL_Event *evt)
+NK_API void
+nk_sdl_handle_grab(void)
 {
     struct nk_context *ctx = &sdl.ctx;
-
-    /* optional grabbing behavior */
     if (ctx->input.mouse.grab) {
         SDL_SetRelativeMouseMode(SDL_TRUE);
-        ctx->input.mouse.grab = 0;
     } else if (ctx->input.mouse.ungrab) {
-        int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y;
+        /* better support for older SDL by setting mode first; causes an extra mouse motion event */
         SDL_SetRelativeMouseMode(SDL_FALSE);
-        SDL_WarpMouseInWindow(sdl.win, x, y);
-        ctx->input.mouse.ungrab = 0;
+        SDL_WarpMouseInWindow(sdl.win, (int)ctx->input.mouse.prev.x, (int)ctx->input.mouse.prev.y);
+    } else if (ctx->input.mouse.grabbed) {
+        ctx->input.mouse.pos.x = ctx->input.mouse.prev.x;
+        ctx->input.mouse.pos.y = ctx->input.mouse.prev.y;
     }
+}
+
+NK_API int
+nk_sdl_handle_event(SDL_Event *evt)
+{
+    struct nk_context *ctx = &sdl.ctx;
 
     switch(evt->type)
     {

+ 1 - 1
demo/sfml_opengl2/Readme.md

@@ -6,4 +6,4 @@ This backend provides support for [SFML 2.4](http://www.sfml-dev.org). It will w
 
 You have to edit the Makefile provided so that you can build the demo. Edit the SFML_DIR variable to point to your SFML root folder. This will be the folder to which SFML was installed and contains the lib and include folders.
 
-On Linux there is an extra step. You need to install the the udev development files.
+On Linux there is an extra step. You need to install the udev development files.

+ 2 - 0
demo/sfml_opengl2/nuklear_sfml_gl2.h

@@ -31,6 +31,8 @@ NK_API void                 nk_sfml_shutdown(void);
  * ===============================================================
  */
  #ifdef NK_SFML_GL2_IMPLEMENTATION
+#include <cstdlib>
+#include <cstring>
 
 struct nk_sfml_device {
     struct nk_buffer cmds;

+ 1 - 1
demo/sfml_opengl3/Readme.md

@@ -8,4 +8,4 @@ This backend uses Glad to handle OpenGL extensions. You can download the Glad fi
 
 Once SFML and Glad have been installed on your system you have to edit the Makefile provided so that you can build the demo. There are two variables that need to be edited: SFML_DIR and GLAD_DIR. Make these point to your SFML root folder and Glad root folder respectively.
 
-On Linux there is an extra step. You need to install the the udev development files.
+On Linux there is an extra step. You need to install the udev development files.

+ 2 - 1
demo/sfml_opengl3/nuklear_sfml_gl3.h

@@ -38,7 +38,8 @@ NK_API void                 nk_sfml_device_destroy(void);
  * ===============================================================
  */
  #ifdef NK_SFML_GL3_IMPLEMENTATION
-
+#include <cstring>
+#include <assert.h>
 #include <string>
 
 struct nk_sfml_device {

+ 4 - 0
demo/wayland_rawfb/nuklear_raw_wayland.h

@@ -1,6 +1,10 @@
 #ifndef NK_RAW_WAYLAND_H_
 #define NK_RAW_WAYLAND_H_
 
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
 #define WIDTH 800
 #define HEIGHT 600
 

+ 3 - 0
demo/x11/nuklear_xlib.h

@@ -44,6 +44,9 @@ NK_API void                 nk_xfont_del(Display *dpy, XFont *font);
  * ===============================================================
  */
 #ifdef NK_XLIB_IMPLEMENTATION
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/Xresource.h>

+ 1 - 0
demo/x11_opengl3/nuklear_xlib_gl3.h

@@ -32,6 +32,7 @@ NK_API void                 nk_x11_device_destroy(void);
  * ===============================================================
  */
 #ifdef NK_XLIB_GL3_IMPLEMENTATION
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>

+ 4 - 0
demo/x11_rawfb/nuklear_rawfb.h

@@ -55,6 +55,10 @@ NK_API void                  nk_rawfb_resize_fb(struct rawfb_context *rawfb, voi
  * ===============================================================
  */
 #ifdef NK_RAWFB_IMPLEMENTATION
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
 struct rawfb_image {
     void *pixels;
     int w, h, pitch;

+ 3 - 0
demo/x11_rawfb/nuklear_xlib.h

@@ -49,6 +49,9 @@ NK_API void nk_xlib_shutdown(void);
  * ===============================================================
  */
 #ifdef NK_XLIBSHM_IMPLEMENTATION
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/Xresource.h>

+ 3 - 0
demo/x11_xft/nuklear_xlib.h

@@ -48,6 +48,9 @@ NK_API void                 nk_xfont_del(Display *dpy, XFont *font);
  * ===============================================================
  */
 #ifdef NK_XLIB_IMPLEMENTATION
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/Xresource.h>

+ 5 - 5
doc/index.html

@@ -886,7 +886,7 @@ NK_WINDOW_NO_INPUT          | Prevents window of scaling, moving or getting focu
 #### nk_collapse_states
 State           | Description
 ----------------|-----------------------------------------------------------
-__NK_MINIMIZED__| UI section is collased and not visible until maximized
+__NK_MINIMIZED__| UI section is collapsed and not visible until maximized
 __NK_MAXIMIZED__| UI section is extended and visible until minimized
 <br /><br />
 #### nk_begin
@@ -1267,7 +1267,7 @@ Parameter   | Description
 __ctx__     | Must point to an previously initialized `nk_context` struct
 __name__    | Identifier of the window to either hide or show
 __state__   | state with either visible or hidden to modify the window with
-__cond__    | condition that has to be met to actually commit the visbility state change
+__cond__    | condition that has to be met to actually commit the visibility state change
 ### Layouting
 Layouting in general describes placing widget inside a window with position and size.
 While in this particular implementation there are five different APIs for layouting
@@ -1306,7 +1306,7 @@ functions should be fine.
     if the owning window grows in width. So the number of columns dictates
     the size of each widget dynamically by formula:
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-    widget_width = (window_width - padding - spacing) * (1/colum_count)
+    widget_width = (window_width - padding - spacing) * (1/column_count)
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     Just like all other layouting APIs if you define more widget than columns this
     library will allocate a new row and keep all layouting parameters previously
@@ -2760,7 +2760,7 @@ X...XXXXXXXXXXXXX...X -           "
                        dynamic and static widgets.
 - 2016/12/31 (1.30.0) - Extended scrollbar offset from 16-bit to 32-bit.
 - 2016/12/31 (1.29.2) - Fixed closing window bug of minimized windows.
-- 2016/12/03 (1.29.1) - Fixed wrapped text with no seperator and C89 error.
+- 2016/12/03 (1.29.1) - Fixed wrapped text with no separator and C89 error.
 - 2016/12/03 (1.29.0) - Changed text wrapping to process words not characters.
 - 2016/11/22 (1.28.6) - Fixed window minimized closing bug.
 - 2016/11/19 (1.28.5) - Fixed abstract combo box closing behavior.
@@ -2898,7 +2898,7 @@ X...XXXXXXXXXXXXX...X -           "
                        precision.
 - 2016/08/08 (1.07.2) - Fixed compiling error without define `NK_INCLUDE_FIXED_TYPE`.
 - 2016/08/08 (1.07.1) - Fixed possible floating point error inside `nk_widget` leading
-                       to wrong wiget width calculation which results in widgets falsely
+                       to wrong widget width calculation which results in widgets falsely
                        becoming tagged as not inside window and cannot be accessed.
 - 2016/08/08 (1.07.0) - Nuklear now differentiates between hiding a window (NK_WINDOW_HIDDEN) and
                        closing a window (NK_WINDOW_CLOSED). A window can be hidden/shown

+ 2 - 1
example/file_browser.c

@@ -345,7 +345,8 @@ media_init(struct media *media)
 static void
 file_browser_reload_directory_content(struct file_browser *browser, const char *path)
 {
-    strncpy(browser->directory, path, MAX_PATH_LEN);
+    const size_t path_len = nk_strlen(path) + 1;
+    NK_MEMCPY(browser->directory, path, MIN(path_len, MAX_PATH_LEN));
     browser->directory[MAX_PATH_LEN - 1] = 0;
     dir_free_list(browser->files, browser->file_count);
     dir_free_list(browser->directories, browser->dir_count);

Разница между файлами не показана из-за своего большого размера
+ 347 - 86
nuklear.h


+ 6 - 3
src/CHANGELOG

@@ -7,7 +7,10 @@
 ///   - [y]: Minor version with non-breaking API and library changes
 ///   - [z]: Patch version with no direct changes to the API
 ///
-/// - 2023/11/03 (4.10.7) - Fix null pointer dereference with nk_group and nk_listview scroll offsets
+/// - 2024/04/04 (4.12.2) - Fix null pointer dereference with nk_group and nk_listview scroll offsets
+/// - 2024/03/07 (4.12.1) - Fix bitwise operations warnings in C++20
+/// - 2023/11/26 (4.12.0) - Added an alignment option to checkboxes and radio buttons.
+/// - 2023/10/11 (4.11.0) - Added nk_widget_disable_begin() and nk_widget_disable_end()
 /// - 2022/12/23 (4.10.6) - Fix incorrect glyph index in nk_font_bake()
 /// - 2022/12/17 (4.10.5) - Fix nk_font_bake_pack() using TTC font offset incorrectly
 /// - 2022/10/24 (4.10.4) - Fix nk_str_{append,insert}_str_utf8 always returning 0
@@ -152,7 +155,7 @@
 ///                        dynamic and static widgets.
 /// - 2016/12/31 (1.30.0) - Extended scrollbar offset from 16-bit to 32-bit.
 /// - 2016/12/31 (1.29.2) - Fixed closing window bug of minimized windows.
-/// - 2016/12/03 (1.29.1) - Fixed wrapped text with no seperator and C89 error.
+/// - 2016/12/03 (1.29.1) - Fixed wrapped text with no separator and C89 error.
 /// - 2016/12/03 (1.29.0) - Changed text wrapping to process words not characters.
 /// - 2016/11/22 (1.28.6) - Fixed window minimized closing bug.
 /// - 2016/11/19 (1.28.5) - Fixed abstract combo box closing behavior.
@@ -290,7 +293,7 @@
 ///                        precision.
 /// - 2016/08/08 (1.07.2) - Fixed compiling error without define `NK_INCLUDE_FIXED_TYPE`.
 /// - 2016/08/08 (1.07.1) - Fixed possible floating point error inside `nk_widget` leading
-///                        to wrong wiget width calculation which results in widgets falsely
+///                        to wrong widget width calculation which results in widgets falsely
 ///                        becoming tagged as not inside window and cannot be accessed.
 /// - 2016/08/08 (1.07.0) - Nuklear now differentiates between hiding a window (NK_WINDOW_HIDDEN) and
 ///                        closing a window (NK_WINDOW_CLOSED). A window can be hidden/shown

+ 62 - 5
src/nuklear.h

@@ -1241,7 +1241,7 @@ NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*
 /// #### nk_collapse_states
 /// State           | Description
 /// ----------------|-----------------------------------------------------------
-/// __NK_MINIMIZED__| UI section is collased and not visible until maximized
+/// __NK_MINIMIZED__| UI section is collapsed and not visible until maximized
 /// __NK_MAXIMIZED__| UI section is extended and visible until minimized
 /// <br /><br />
 */
@@ -1779,11 +1779,11 @@ NK_API void nk_window_show(struct nk_context*, const char *name, enum nk_show_st
 /// __ctx__     | Must point to an previously initialized `nk_context` struct
 /// __name__    | Identifier of the window to either hide or show
 /// __state__   | state with either visible or hidden to modify the window with
-/// __cond__    | condition that has to be met to actually commit the visbility state change
+/// __cond__    | condition that has to be met to actually commit the visibility state change
 */
 NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond);
 /*/// #### nk_window_show_if
-/// Line for visual seperation. Draws a line with thickness determined by the current row height.
+/// Line for visual separation. Draws a line with thickness determined by the current row height.
 /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
 /// void nk_rule_horizontal(struct nk_context *ctx, struct nk_color color, NK_BOOL rounding)
 /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1847,7 +1847,7 @@ NK_API void nk_rule_horizontal(struct nk_context *ctx, struct nk_color color, nk
 ///     the size of each widget dynamically by formula:
 ///
 ///     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-///     widget_width = (window_width - padding - spacing) * (1/colum_count)
+///     widget_width = (window_width - padding - spacing) * (1/column_count)
 ///     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ///
 ///     Just like all other layouting APIs if you define more widget than columns this
@@ -2068,6 +2068,21 @@ NK_API void nk_rule_horizontal(struct nk_context *ctx, struct nk_color color, nk
 /// nk_layout_space_rect_to_screen          | Converts rectangle from nk_layout_space coordinate space into screen space
 /// nk_layout_space_rect_to_local           | Converts rectangle from screen space into nk_layout_space coordinates
 */
+
+enum nk_widget_align {
+    NK_WIDGET_ALIGN_LEFT        = 0x01,
+    NK_WIDGET_ALIGN_CENTERED    = 0x02,
+    NK_WIDGET_ALIGN_RIGHT       = 0x04,
+    NK_WIDGET_ALIGN_TOP         = 0x08,
+    NK_WIDGET_ALIGN_MIDDLE      = 0x10,
+    NK_WIDGET_ALIGN_BOTTOM      = 0x20
+};
+enum nk_widget_alignment {
+    NK_WIDGET_LEFT        = NK_WIDGET_ALIGN_MIDDLE|NK_WIDGET_ALIGN_LEFT,
+    NK_WIDGET_CENTERED    = NK_WIDGET_ALIGN_MIDDLE|NK_WIDGET_ALIGN_CENTERED,
+    NK_WIDGET_RIGHT       = NK_WIDGET_ALIGN_MIDDLE|NK_WIDGET_ALIGN_RIGHT
+};
+
 /*/// #### nk_layout_set_min_row_height
 /// Sets the currently used minimum row height.
 /// !!! WARNING
@@ -2867,7 +2882,8 @@ NK_API void nk_list_view_end(struct nk_list_view*);
 enum nk_widget_layout_states {
     NK_WIDGET_INVALID, /* The widget cannot be seen and is completely out of view */
     NK_WIDGET_VALID, /* The widget is completely inside the window and can be updated and drawn */
-    NK_WIDGET_ROM /* The widget is partially visible and cannot be updated */
+    NK_WIDGET_ROM, /* The widget is partially visible and cannot be updated */
+    NK_WIDGET_DISABLED /* The widget is manually disabled and acts like NK_WIDGET_ROM */
 };
 enum nk_widget_states {
     NK_WIDGET_STATE_MODIFIED    = NK_FLAG(1),
@@ -2890,6 +2906,8 @@ NK_API nk_bool nk_widget_is_hovered(struct nk_context*);
 NK_API nk_bool nk_widget_is_mouse_clicked(struct nk_context*, enum nk_buttons);
 NK_API nk_bool nk_widget_has_mouse_click_down(struct nk_context*, enum nk_buttons, nk_bool down);
 NK_API void nk_spacing(struct nk_context*, int cols);
+NK_API void nk_widget_disable_begin(struct nk_context* ctx);
+NK_API void nk_widget_disable_end(struct nk_context* ctx);
 /* =============================================================================
  *
  *                                  TEXT
@@ -2967,10 +2985,13 @@ NK_API nk_bool nk_button_pop_behavior(struct nk_context*);
  * ============================================================================= */
 NK_API nk_bool nk_check_label(struct nk_context*, const char*, nk_bool active);
 NK_API nk_bool nk_check_text(struct nk_context*, const char*, int, nk_bool active);
+NK_API nk_bool nk_check_text_align(struct nk_context*, const char*, int, nk_bool active, nk_flags widget_alignment, nk_flags text_alignment);
 NK_API unsigned nk_check_flags_label(struct nk_context*, const char*, unsigned int flags, unsigned int value);
 NK_API unsigned nk_check_flags_text(struct nk_context*, const char*, int, unsigned int flags, unsigned int value);
 NK_API nk_bool nk_checkbox_label(struct nk_context*, const char*, nk_bool *active);
+NK_API nk_bool nk_checkbox_label_align(struct nk_context *ctx, const char *label, nk_bool *active, nk_flags widget_alignment, nk_flags text_alignment);
 NK_API nk_bool nk_checkbox_text(struct nk_context*, const char*, int, nk_bool *active);
+NK_API nk_bool nk_checkbox_text_align(struct nk_context *ctx, const char *text, int len, nk_bool *active, nk_flags widget_alignment, nk_flags text_alignment);
 NK_API nk_bool nk_checkbox_flags_label(struct nk_context*, const char*, unsigned int *flags, unsigned int value);
 NK_API nk_bool nk_checkbox_flags_text(struct nk_context*, const char*, int, unsigned int *flags, unsigned int value);
 /* =============================================================================
@@ -2979,9 +3000,13 @@ NK_API nk_bool nk_checkbox_flags_text(struct nk_context*, const char*, int, unsi
  *
  * ============================================================================= */
 NK_API nk_bool nk_radio_label(struct nk_context*, const char*, nk_bool *active);
+NK_API nk_bool nk_radio_label_align(struct nk_context *ctx, const char *label, nk_bool *active, nk_flags widget_alignment, nk_flags text_alignment);
 NK_API nk_bool nk_radio_text(struct nk_context*, const char*, int, nk_bool *active);
+NK_API nk_bool nk_radio_text_align(struct nk_context *ctx, const char *text, int len, nk_bool *active, nk_flags widget_alignment, nk_flags text_alignment);
 NK_API nk_bool nk_option_label(struct nk_context*, const char*, nk_bool active);
+NK_API nk_bool nk_option_label_align(struct nk_context *ctx, const char *label, nk_bool active, nk_flags widget_alignment, nk_flags text_alignment);
 NK_API nk_bool nk_option_text(struct nk_context*, const char*, int, nk_bool active);
+NK_API nk_bool nk_option_text_align(struct nk_context *ctx, const char *text, int len, nk_bool is_active, nk_flags widget_alignment, nk_flags text_alignment);
 /* =============================================================================
  *
  *                                  SELECTABLE
@@ -3384,6 +3409,9 @@ NK_API void nk_menu_end(struct nk_context*);
  *                                  STYLE
  *
  * ============================================================================= */
+
+#define NK_WIDGET_DISABLED_FACTOR 0.5f
+
 enum nk_style_colors {
     NK_COLOR_TEXT,
     NK_COLOR_WINDOW,
@@ -3460,6 +3488,7 @@ NK_API struct nk_color nk_rgb_f(float r, float g, float b);
 NK_API struct nk_color nk_rgb_fv(const float *rgb);
 NK_API struct nk_color nk_rgb_cf(struct nk_colorf c);
 NK_API struct nk_color nk_rgb_hex(const char *rgb);
+NK_API struct nk_color nk_rgb_factor(struct nk_color col, const float factor);
 
 NK_API struct nk_color nk_rgba(int r, int g, int b, int a);
 NK_API struct nk_color nk_rgba_u32(nk_uint);
@@ -4680,6 +4709,8 @@ struct nk_style_item {
 struct nk_style_text {
     struct nk_color color;
     struct nk_vec2 padding;
+    float color_factor;
+    float disabled_factor;
 };
 
 struct nk_style_button {
@@ -4688,6 +4719,7 @@ struct nk_style_button {
     struct nk_style_item hover;
     struct nk_style_item active;
     struct nk_color border_color;
+    float color_factor_background;
 
     /* text */
     struct nk_color text_background;
@@ -4695,6 +4727,7 @@ struct nk_style_button {
     struct nk_color text_hover;
     struct nk_color text_active;
     nk_flags text_alignment;
+    float color_factor_text;
 
     /* properties */
     float border;
@@ -4702,6 +4735,7 @@ struct nk_style_button {
     struct nk_vec2 padding;
     struct nk_vec2 image_padding;
     struct nk_vec2 touch_padding;
+    float disabled_factor;
 
     /* optional user callbacks */
     nk_handle userdata;
@@ -4732,6 +4766,8 @@ struct nk_style_toggle {
     struct nk_vec2 touch_padding;
     float spacing;
     float border;
+    float color_factor;
+    float disabled_factor;
 
     /* optional user callbacks */
     nk_handle userdata;
@@ -4767,6 +4803,8 @@ struct nk_style_selectable {
     struct nk_vec2 padding;
     struct nk_vec2 touch_padding;
     struct nk_vec2 image_padding;
+    float color_factor;
+    float disabled_factor;
 
     /* optional user callbacks */
     nk_handle userdata;
@@ -4799,6 +4837,8 @@ struct nk_style_slider {
     struct nk_vec2 padding;
     struct nk_vec2 spacing;
     struct nk_vec2 cursor_size;
+    float color_factor;
+    float disabled_factor;
 
     /* optional buttons */
     int show_buttons;
@@ -4832,6 +4872,8 @@ struct nk_style_progress {
     float cursor_border;
     float cursor_rounding;
     struct nk_vec2 padding;
+    float color_factor;
+    float disabled_factor;
 
     /* optional user callbacks */
     nk_handle userdata;
@@ -4858,6 +4900,8 @@ struct nk_style_scrollbar {
     float border_cursor;
     float rounding_cursor;
     struct nk_vec2 padding;
+    float color_factor;
+    float disabled_factor;
 
     /* optional buttons */
     int show_buttons;
@@ -4904,6 +4948,8 @@ struct nk_style_edit {
     struct nk_vec2 scrollbar_size;
     struct nk_vec2 padding;
     float row_padding;
+    float color_factor;
+    float disabled_factor;
 };
 
 struct nk_style_property {
@@ -4926,6 +4972,8 @@ struct nk_style_property {
     float border;
     float rounding;
     struct nk_vec2 padding;
+    float color_factor;
+    float disabled_factor;
 
     struct nk_style_edit edit;
     struct nk_style_button inc_button;
@@ -4948,6 +4996,9 @@ struct nk_style_chart {
     float border;
     float rounding;
     struct nk_vec2 padding;
+    float color_factor;
+    float disabled_factor;
+    nk_bool show_markers;
 };
 
 struct nk_style_combo {
@@ -4979,6 +5030,8 @@ struct nk_style_combo {
     struct nk_vec2 content_padding;
     struct nk_vec2 button_padding;
     struct nk_vec2 spacing;
+    float color_factor;
+    float disabled_factor;
 };
 
 struct nk_style_tab {
@@ -5001,6 +5054,8 @@ struct nk_style_tab {
     float indent;
     struct nk_vec2 padding;
     struct nk_vec2 spacing;
+    float color_factor;
+    float disabled_factor;
 };
 
 enum nk_style_header_align {
@@ -5134,6 +5189,7 @@ struct nk_chart_slot {
     int count;
     struct nk_vec2 last;
     int index;
+    nk_bool show_markers;
 };
 
 struct nk_chart {
@@ -5283,6 +5339,7 @@ struct nk_window {
     struct nk_popup_state popup;
     struct nk_edit_state edit;
     unsigned int scrolled;
+    nk_bool widgets_disabled;
 
     struct nk_table *tables;
     unsigned int table_count;

+ 23 - 16
src/nuklear_button.c

@@ -98,16 +98,16 @@ nk_draw_button(struct nk_command_buffer *out,
         background = &style->active;
     else background = &style->normal;
 
-    switch(background->type) {
+    switch (background->type) {
         case NK_STYLE_ITEM_IMAGE:
-            nk_draw_image(out, *bounds, &background->data.image, nk_white);
+            nk_draw_image(out, *bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor_background));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
-            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white);
+            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor_background));
             break;
         case NK_STYLE_ITEM_COLOR:
-            nk_fill_rect(out, *bounds, style->rounding, background->data.color);
-            nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color);
+            nk_fill_rect(out, *bounds, style->rounding, nk_rgb_factor(background->data.color, style->color_factor_background));
+            nk_stroke_rect(out, *bounds, style->rounding, style->border, nk_rgb_factor(style->border_color, style->color_factor_background));
             break;
     }
     return background;
@@ -127,8 +127,8 @@ nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r,
     /* calculate button content space */
     content->x = r.x + style->padding.x + style->border + style->rounding;
     content->y = r.y + style->padding.y + style->border + style->rounding;
-    content->w = r.w - (2 * style->padding.x + style->border + style->rounding*2);
-    content->h = r.h - (2 * style->padding.y + style->border + style->rounding*2);
+    content->w = r.w - (2 * (style->padding.x + style->border + style->rounding));
+    content->h = r.h - (2 * (style->padding.y + style->border + style->rounding));
 
     /* execute button behavior */
     bounds.x = r.x - style->touch_padding.x;
@@ -157,6 +157,8 @@ nk_draw_button_text(struct nk_command_buffer *out,
         text.text = style->text_active;
     else text.text = style->text_normal;
 
+    text.text = nk_rgb_factor(text.text, style->color_factor_text);
+
     text.padding = nk_vec2(0,0);
     nk_widget_text(out, *content, txt, len, &text, text_alignment, font);
 }
@@ -204,6 +206,8 @@ nk_draw_button_symbol(struct nk_command_buffer *out,
     else if (state & NK_WIDGET_STATE_ACTIVED)
         sym = style->text_active;
     else sym = style->text_normal;
+
+    sym = nk_rgb_factor(sym, style->color_factor_text);
     nk_draw_symbol(out, type, *content, bg, sym, 1, font);
 }
 NK_LIB nk_bool
@@ -235,7 +239,7 @@ nk_draw_button_image(struct nk_command_buffer *out,
     nk_flags state, const struct nk_style_button *style, const struct nk_image *img)
 {
     nk_draw_button(out, bounds, state, style);
-    nk_draw_image(out, *content, img, nk_white);
+    nk_draw_image(out, *content, img, nk_rgb_factor(nk_white, style->color_factor_background));
 }
 NK_LIB nk_bool
 nk_do_button_image(nk_flags *state,
@@ -292,6 +296,8 @@ nk_draw_button_text_symbol(struct nk_command_buffer *out,
         text.text = style->text_normal;
     }
 
+    sym = nk_rgb_factor(sym, style->color_factor_text);
+    text.text = nk_rgb_factor(text.text, style->color_factor_text);
     text.padding = nk_vec2(0,0);
     nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font);
     nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font);
@@ -349,9 +355,10 @@ nk_draw_button_text_image(struct nk_command_buffer *out,
         text.text = style->text_active;
     else text.text = style->text_normal;
 
-    text.padding = nk_vec2(0,0);
+    text.text = nk_rgb_factor(text.text, style->color_factor_text);
+    text.padding = nk_vec2(0, 0);
     nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font);
-    nk_draw_image(out, *image, img, nk_white);
+    nk_draw_image(out, *image, img, nk_rgb_factor(nk_white, style->color_factor_background));
 }
 NK_LIB nk_bool
 nk_do_button_text_image(nk_flags *state,
@@ -456,7 +463,7 @@ nk_button_text_styled(struct nk_context *ctx,
     state = nk_widget(&bounds, ctx);
 
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds,
                     title, len, style->text_alignment, ctx->button_behavior,
                     style, in, ctx->style.font);
@@ -501,7 +508,7 @@ nk_button_color(struct nk_context *ctx, struct nk_color color)
 
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
 
     button = ctx->style.button;
     button.normal = nk_style_item_color(color);
@@ -533,7 +540,7 @@ nk_button_symbol_styled(struct nk_context *ctx,
     layout = win->layout;
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds,
             symbol, ctx->button_behavior, style, in, ctx->style.font);
 }
@@ -566,7 +573,7 @@ nk_button_image_styled(struct nk_context *ctx, const struct nk_style_button *sty
 
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_button_image(&ctx->last_widget_state, &win->buffer, bounds,
                 img, ctx->button_behavior, style, in);
 }
@@ -600,7 +607,7 @@ nk_button_symbol_text_styled(struct nk_context *ctx,
 
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds,
                 symbol, text, len, align, ctx->button_behavior,
                 style, ctx->style.font, in);
@@ -647,7 +654,7 @@ nk_button_image_text_styled(struct nk_context *ctx,
 
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_button_text_image(&ctx->last_widget_state, &win->buffer,
             bounds, img, text, len, align, ctx->button_behavior,
             style, ctx->style.font, in);

+ 24 - 14
src/nuklear_chart.c

@@ -48,26 +48,27 @@ nk_chart_begin_colored(struct nk_context *ctx, enum nk_chart_type type,
     {struct nk_chart_slot *slot = &chart->slots[chart->slot++];
     slot->type = type;
     slot->count = count;
-    slot->color = color;
+    slot->color = nk_rgb_factor(color, style->color_factor);
     slot->highlight = highlight;
     slot->min = NK_MIN(min_value, max_value);
     slot->max = NK_MAX(min_value, max_value);
-    slot->range = slot->max - slot->min;}
+    slot->range = slot->max - slot->min;
+    slot->show_markers = style->show_markers;}
 
     /* draw chart background */
     background = &style->background;
 
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
-            nk_draw_image(&win->buffer, bounds, &background->data.image, nk_white);
+            nk_draw_image(&win->buffer, bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
-            nk_draw_nine_slice(&win->buffer, bounds, &background->data.slice, nk_white);
+            nk_draw_nine_slice(&win->buffer, bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
-            nk_fill_rect(&win->buffer, bounds, style->rounding, style->border_color);
+            nk_fill_rect(&win->buffer, bounds, style->rounding, nk_rgb_factor(style->border_color, style->color_factor));
             nk_fill_rect(&win->buffer, nk_shrink_rect(bounds, style->border),
-                style->rounding, style->background.data.color);
+                style->rounding, nk_rgb_factor(style->background.data.color, style->color_factor));
             break;
     }
     return 1;
@@ -84,6 +85,8 @@ nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type,
     struct nk_color color, struct nk_color highlight,
     int count, float min_value, float max_value)
 {
+    const struct nk_style_chart* style;
+
     NK_ASSERT(ctx);
     NK_ASSERT(ctx->current);
     NK_ASSERT(ctx->current->layout);
@@ -91,16 +94,19 @@ nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type,
     if (!ctx || !ctx->current || !ctx->current->layout) return;
     if (ctx->current->layout->chart.slot >= NK_CHART_MAX_SLOT) return;
 
+    style = &ctx->style.chart;
+
     /* add another slot into the graph */
     {struct nk_chart *chart = &ctx->current->layout->chart;
     struct nk_chart_slot *slot = &chart->slots[chart->slot++];
     slot->type = type;
     slot->count = count;
-    slot->color = color;
+    slot->color = nk_rgb_factor(color, style->color_factor);
     slot->highlight = highlight;
     slot->min = NK_MIN(min_value, max_value);
     slot->max = NK_MAX(min_value, max_value);
-    slot->range = slot->max - slot->min;}
+    slot->range = slot->max - slot->min;
+    slot->show_markers = style->show_markers;}
 }
 NK_API void
 nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type type,
@@ -114,7 +120,7 @@ nk_chart_push_line(struct nk_context *ctx, struct nk_window *win,
     struct nk_chart *g, float value, int slot)
 {
     struct nk_panel *layout = win->layout;
-    const struct nk_input *i = &ctx->input;
+    const struct nk_input *i = ctx->current->widgets_disabled ? 0 : &ctx->input;
     struct nk_command_buffer *out = &win->buffer;
 
     nk_flags ret = 0;
@@ -140,14 +146,16 @@ nk_chart_push_line(struct nk_context *ctx, struct nk_window *win,
         bounds.w = bounds.h = 4;
 
         color = g->slots[slot].color;
-        if (!(layout->flags & NK_WINDOW_ROM) &&
+        if (!(layout->flags & NK_WINDOW_ROM) && i &&
             NK_INBOX(i->mouse.pos.x,i->mouse.pos.y, g->slots[slot].last.x-3, g->slots[slot].last.y-3, 6, 6)){
             ret = nk_input_is_mouse_hovering_rect(i, bounds) ? NK_CHART_HOVERING : 0;
             ret |= (i->mouse.buttons[NK_BUTTON_LEFT].down &&
                 i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0;
             color = g->slots[slot].highlight;
         }
-        nk_fill_rect(out, bounds, 0, color);
+        if (g->slots[slot].show_markers) {
+            nk_fill_rect(out, bounds, 0, color);
+        }
         g->slots[slot].index += 1;
         return ret;
     }
@@ -171,7 +179,9 @@ nk_chart_push_line(struct nk_context *ctx, struct nk_window *win,
             color = g->slots[slot].highlight;
         }
     }
-    nk_fill_rect(out, nk_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color);
+    if (g->slots[slot].show_markers) {
+        nk_fill_rect(out, nk_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color);
+    }
 
     /* save current data point position */
     g->slots[slot].last.x = cur.x;
@@ -184,7 +194,7 @@ nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win,
     struct nk_chart *chart, float value, int slot)
 {
     struct nk_command_buffer *out = &win->buffer;
-    const struct nk_input *in = &ctx->input;
+    const struct nk_input *in = ctx->current->widgets_disabled ? 0 : &ctx->input;
     struct nk_panel *layout = win->layout;
 
     float ratio;
@@ -214,7 +224,7 @@ nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win,
     item.x = item.x + ((float)chart->slots[slot].index);
 
     /* user chart bar selection */
-    if (!(layout->flags & NK_WINDOW_ROM) &&
+    if (!(layout->flags & NK_WINDOW_ROM) && in &&
         NK_INBOX(in->mouse.pos.x,in->mouse.pos.y,item.x,item.y,item.w,item.h)) {
         ret = NK_CHART_HOVERING;
         ret |= (!in->mouse.buttons[NK_BUTTON_LEFT].down &&

+ 10 - 0
src/nuklear_color.c

@@ -23,6 +23,16 @@ nk_parse_hex(const char *p, int length)
     return i;
 }
 NK_API struct nk_color
+nk_rgb_factor(struct nk_color col, const float factor)
+{
+    if (factor == 1.0f)
+        return col;
+    col.r = (nk_byte)(col.r * factor);
+    col.g = (nk_byte)(col.g * factor);
+    col.b = (nk_byte)(col.b * factor);
+    return col;
+}
+NK_API struct nk_color
 nk_rgba(int r, int g, int b, int a)
 {
     struct nk_color ret;

+ 1 - 1
src/nuklear_color_picker.c

@@ -187,7 +187,7 @@ nk_color_pick(struct nk_context * ctx, struct nk_colorf *color,
     layout = win->layout;
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_color_picker(&ctx->last_widget_state, &win->buffer, color, fmt, bounds,
                 nk_vec2(0,0), in, config->font);
 }

+ 42 - 33
src/nuklear_combo.c

@@ -67,7 +67,7 @@ nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len,
     if (s == NK_WIDGET_INVALID)
         return 0;
 
-    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_DISABLED || s == NK_WIDGET_ROM)? 0: &ctx->input;
     if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
         is_clicked = nk_true;
 
@@ -83,19 +83,21 @@ nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len,
         text.text = style->combo.label_normal;
     }
 
+    text.text = nk_rgb_factor(text.text, style->combo.color_factor);
+
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+            nk_draw_image(&win->buffer, header, &background->data.image, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white);
+            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
             text.background = background->data.color;
-            nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
-            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+            nk_fill_rect(&win->buffer, header, style->combo.rounding, nk_rgb_factor(background->data.color, style->combo.color_factor));
+            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, nk_rgb_factor(style->combo.border_color, style->combo.color_factor));
             break;
     }
     {
@@ -175,7 +177,7 @@ nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_ve
     if (s == NK_WIDGET_INVALID)
         return 0;
 
-    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_DISABLED || s == NK_WIDGET_ROM)? 0: &ctx->input;
     if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
         is_clicked = nk_true;
 
@@ -188,14 +190,14 @@ nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_ve
 
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
-            nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+            nk_draw_image(&win->buffer, header, &background->data.image, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
-            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white);
+            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
-            nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
-            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+            nk_fill_rect(&win->buffer, header, style->combo.rounding, nk_rgb_factor(background->data.color, style->combo.color_factor));
+            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, nk_rgb_factor(style->combo.border_color, style->combo.color_factor));
             break;
     }
     {
@@ -233,7 +235,7 @@ nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_ve
             bounds.w = (button.x - (style->combo.content_padding.x + style->combo.spacing.x)) - bounds.x;
         else
             bounds.w = header.w - 4 * style->combo.content_padding.x;
-        nk_fill_rect(&win->buffer, bounds, 0, color);
+        nk_fill_rect(&win->buffer, bounds, 0, nk_rgb_factor(color, style->combo.color_factor));
 
         /* draw open/close button */
         if (draw_button_symbol)
@@ -268,7 +270,7 @@ nk_combo_begin_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, struct
     if (s == NK_WIDGET_INVALID)
         return 0;
 
-    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_DISABLED || s == NK_WIDGET_ROM)? 0: &ctx->input;
     if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
         is_clicked = nk_true;
 
@@ -284,19 +286,21 @@ nk_combo_begin_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, struct
         symbol_color = style->combo.symbol_hover;
     }
 
+    symbol_color = nk_rgb_factor(symbol_color, style->combo.color_factor);
+
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
             sym_background = nk_rgba(0, 0, 0, 0);
-            nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+            nk_draw_image(&win->buffer, header, &background->data.image, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
             sym_background = nk_rgba(0, 0, 0, 0);
-            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white);
+            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
             sym_background = background->data.color;
-            nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
-            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+            nk_fill_rect(&win->buffer, header, style->combo.rounding, nk_rgb_factor(background->data.color, style->combo.color_factor));
+            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, nk_rgb_factor(style->combo.border_color, style->combo.color_factor));
             break;
     }
     {
@@ -362,7 +366,7 @@ nk_combo_begin_symbol_text(struct nk_context *ctx, const char *selected, int len
     s = nk_widget(&header, ctx);
     if (!s) return 0;
 
-    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_DISABLED || s == NK_WIDGET_ROM)? 0: &ctx->input;
     if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
         is_clicked = nk_true;
 
@@ -381,19 +385,22 @@ nk_combo_begin_symbol_text(struct nk_context *ctx, const char *selected, int len
         text.text = style->combo.label_normal;
     }
 
+    text.text = nk_rgb_factor(text.text, style->combo.color_factor);
+    symbol_color = nk_rgb_factor(symbol_color, style->combo.color_factor);
+
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+            nk_draw_image(&win->buffer, header, &background->data.image, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white);
+            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
             text.background = background->data.color;
-            nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
-            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+            nk_fill_rect(&win->buffer, header, style->combo.rounding, nk_rgb_factor(background->data.color, style->combo.color_factor));
+            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, nk_rgb_factor(style->combo.border_color, style->combo.color_factor));
             break;
     }
     {
@@ -464,7 +471,7 @@ nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2
     if (s == NK_WIDGET_INVALID)
         return 0;
 
-    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_DISABLED || s == NK_WIDGET_ROM)? 0: &ctx->input;
     if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
         is_clicked = nk_true;
 
@@ -477,14 +484,14 @@ nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2
 
     switch (background->type) {
         case NK_STYLE_ITEM_IMAGE:
-            nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+            nk_draw_image(&win->buffer, header, &background->data.image, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
-            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white);
+            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
-            nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
-            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+            nk_fill_rect(&win->buffer, header, style->combo.rounding, nk_rgb_factor(background->data.color, style->combo.color_factor));
+            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, nk_rgb_factor(style->combo.border_color, style->combo.color_factor));
             break;
     }
     {
@@ -522,7 +529,7 @@ nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2
             bounds.w = (button.x - style->combo.content_padding.y) - bounds.x;
         else
             bounds.w = header.w - 2 * style->combo.content_padding.x;
-        nk_draw_image(&win->buffer, bounds, &img, nk_white);
+        nk_draw_image(&win->buffer, bounds, &img, nk_rgb_factor(nk_white, style->combo.color_factor));
 
         /* draw open/close button */
         if (draw_button_symbol)
@@ -556,7 +563,7 @@ nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len,
     s = nk_widget(&header, ctx);
     if (!s) return 0;
 
-    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_DISABLED || s == NK_WIDGET_ROM)? 0: &ctx->input;
     if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
         is_clicked = nk_true;
 
@@ -572,19 +579,21 @@ nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len,
         text.text = style->combo.label_normal;
     }
 
+    text.text = nk_rgb_factor(text.text, style->combo.color_factor);
+
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+            nk_draw_image(&win->buffer, header, &background->data.image, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white);
+            nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_rgb_factor(nk_white, style->combo.color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
             text.background = background->data.color;
-            nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
-            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+            nk_fill_rect(&win->buffer, header, style->combo.rounding, nk_rgb_factor(background->data.color, style->combo.color_factor));
+            nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, nk_rgb_factor(style->combo.border_color, style->combo.color_factor));
             break;
     }
     {
@@ -623,7 +632,7 @@ nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len,
         image.y = header.y + style->combo.content_padding.y;
         image.h = header.h - 2 * style->combo.content_padding.y;
         image.w = image.h;
-        nk_draw_image(&win->buffer, image, &img, nk_white);
+        nk_draw_image(&win->buffer, image, &img, nk_rgb_factor(nk_white, style->combo.color_factor));
 
         /* draw label */
         text.padding = nk_vec2(0,0);

+ 32 - 27
src/nuklear_contextual.c

@@ -13,6 +13,7 @@ nk_contextual_begin(struct nk_context *ctx, nk_flags flags, struct nk_vec2 size,
     struct nk_window *win;
     struct nk_window *popup;
     struct nk_rect body;
+    struct nk_input* in;
 
     NK_STORAGE const struct nk_rect null_rect = {-1,-1,0,0};
     int is_clicked = 0;
@@ -33,35 +34,39 @@ nk_contextual_begin(struct nk_context *ctx, nk_flags flags, struct nk_vec2 size,
     /* check if currently active contextual is active */
     popup = win->popup.win;
     is_open = (popup && win->popup.type == NK_PANEL_CONTEXTUAL);
-    is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds);
-    if (win->popup.active_con && win->popup.con_count != win->popup.active_con)
-        return 0;
-    if (!is_open && win->popup.active_con)
-        win->popup.active_con = 0;
-    if ((!is_open && !is_clicked))
-        return 0;
+    in = win->widgets_disabled ? 0 : &ctx->input;
+    if (in) {
+        is_clicked = nk_input_mouse_clicked(in, NK_BUTTON_RIGHT, trigger_bounds);
+        if (win->popup.active_con && win->popup.con_count != win->popup.active_con)
+            return 0;
+        if (!is_open && win->popup.active_con)
+            win->popup.active_con = 0;
+        if ((!is_open && !is_clicked))
+            return 0;
+
+        /* calculate contextual position on click */
+        win->popup.active_con = win->popup.con_count;
+        if (is_clicked) {
+            body.x = in->mouse.pos.x;
+            body.y = in->mouse.pos.y;
+        } else {
+            body.x = popup->bounds.x;
+            body.y = popup->bounds.y;
+        }
 
-    /* calculate contextual position on click */
-    win->popup.active_con = win->popup.con_count;
-    if (is_clicked) {
-        body.x = ctx->input.mouse.pos.x;
-        body.y = ctx->input.mouse.pos.y;
-    } else {
-        body.x = popup->bounds.x;
-        body.y = popup->bounds.y;
-    }
-    body.w = size.x;
-    body.h = size.y;
+        body.w = size.x;
+        body.h = size.y;
 
-    /* start nonblocking contextual popup */
-    ret = nk_nonblock_begin(ctx, flags|NK_WINDOW_NO_SCROLLBAR, body,
+        /* start nonblocking contextual popup */
+        ret = nk_nonblock_begin(ctx, flags | NK_WINDOW_NO_SCROLLBAR, body,
             null_rect, NK_PANEL_CONTEXTUAL);
-    if (ret) win->popup.type = NK_PANEL_CONTEXTUAL;
-    else {
-        win->popup.active_con = 0;
-        win->popup.type = NK_PANEL_NONE;
-        if (win->popup.win)
-            win->popup.win->flags = 0;
+        if (ret) win->popup.type = NK_PANEL_CONTEXTUAL;
+        else {
+            win->popup.active_con = 0;
+            win->popup.type = NK_PANEL_NONE;
+            if (win->popup.win)
+                win->popup.win->flags = 0;
+        }
     }
     return ret;
 }
@@ -193,7 +198,7 @@ nk_contextual_end(struct nk_context *ctx)
     popup = ctx->current;
     panel = popup->layout;
     NK_ASSERT(popup->parent);
-    NK_ASSERT(panel->type & NK_PANEL_SET_POPUP);
+    NK_ASSERT((int)panel->type & (int)NK_PANEL_SET_POPUP);
     if (panel->flags & NK_WINDOW_DYNAMIC) {
         /* Close behavior
         This is a bit of a hack solution since we do not know before we end our popup

+ 15 - 4
src/nuklear_edit.c

@@ -94,6 +94,9 @@ nk_edit_draw_text(struct nk_command_buffer *out,
     txt.background = background;
     txt.text = foreground;
 
+    foreground = nk_rgb_factor(foreground, style->color_factor);
+    background = nk_rgb_factor(background, style->color_factor);
+
     glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len);
     if (!glyph_len) return;
     while ((text_len < byte_len) && glyph_len)
@@ -331,14 +334,14 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
     /* draw background frame */
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
-            nk_draw_image(out, bounds, &background->data.image, nk_white);
+            nk_draw_image(out, bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
-            nk_draw_nine_slice(out, bounds, &background->data.slice, nk_white);
+            nk_draw_nine_slice(out, bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
-            nk_fill_rect(out, bounds, style->rounding, background->data.color);
-            nk_stroke_rect(out, bounds, style->rounding, style->border, style->border_color);
+            nk_fill_rect(out, bounds, style->rounding, nk_rgb_factor(background->data.color, style->color_factor));
+            nk_stroke_rect(out, bounds, style->rounding, style->border, nk_rgb_factor(style->border_color, style->color_factor));
             break;
     }}
 
@@ -547,6 +550,8 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
         else
             background_color = background->data.color;
 
+        cursor_color = nk_rgb_factor(cursor_color, style->color_factor);
+        cursor_text_color = nk_rgb_factor(cursor_text_color, style->color_factor);
 
         if (edit->select_start == edit->select_end) {
             /* no selection so just draw the complete text */
@@ -654,6 +659,10 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
             background_color = nk_rgba(0,0,0,0);
         else
             background_color = background->data.color;
+
+        background_color = nk_rgb_factor(background_color, style->color_factor);
+        text_color = nk_rgb_factor(text_color, style->color_factor);
+
         nk_edit_draw_text(out, style, area.x - edit->scrollbar.x,
             area.y - edit->scrollbar.y, 0, begin, l, row_height, font,
             background_color, text_color, nk_false);
@@ -773,6 +782,8 @@ nk_edit_buffer(struct nk_context *ctx, nk_flags flags,
     style = &ctx->style;
     state = nk_widget(&bounds, ctx);
     if (!state) return state;
+    else if (state == NK_WIDGET_DISABLED)
+        flags |= NK_EDIT_READ_ONLY;
     in = (win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
 
     /* check if edit is currently hot item */

+ 7 - 7
src/nuklear_internal.h

@@ -255,9 +255,9 @@ enum nk_toggle_type {
     NK_TOGGLE_OPTION
 };
 NK_LIB nk_bool nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, nk_flags *state, nk_bool active);
-NK_LIB void nk_draw_checkbox(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font);
-NK_LIB void nk_draw_option(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font);
-NK_LIB nk_bool nk_do_toggle(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, nk_bool *active, const char *str, int len, enum nk_toggle_type type, const struct nk_style_toggle *style, const struct nk_input *in, const struct nk_user_font *font);
+NK_LIB void nk_draw_checkbox(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font, nk_flags text_alignment);
+NK_LIB void nk_draw_option(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font, nk_flags text_alignment);
+NK_LIB nk_bool nk_do_toggle(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, nk_bool *active, const char *str, int len, enum nk_toggle_type type, const struct nk_style_toggle *style, const struct nk_input *in, const struct nk_user_font *font, nk_flags widget_alignment, nk_flags text_alignment);
 
 /* progress */
 NK_LIB nk_size nk_progress_behavior(nk_flags *state, struct nk_input *in, struct nk_rect r, struct nk_rect cursor, nk_size max, nk_size value, nk_bool modifiable);
@@ -350,14 +350,14 @@ NK_LIB void nk_property(struct nk_context *ctx, const char *name, struct nk_prop
 #ifndef STBTT_malloc
 static void*
 nk_stbtt_malloc(nk_size size, void *user_data) {
-	struct nk_allocator *alloc = (struct nk_allocator *) user_data;
-	return alloc->alloc(alloc->userdata, 0, size);
+    struct nk_allocator *alloc = (struct nk_allocator *) user_data;
+    return alloc->alloc(alloc->userdata, 0, size);
 }
 
 static void
 nk_stbtt_free(void *ptr, void *user_data) {
-	struct nk_allocator *alloc = (struct nk_allocator *) user_data;
-	alloc->free(alloc->userdata, ptr);
+    struct nk_allocator *alloc = (struct nk_allocator *) user_data;
+    alloc->free(alloc->userdata, ptr);
 }
 
 #define STBTT_malloc(x,u)  nk_stbtt_malloc(x,u)

+ 5 - 5
src/nuklear_menu.c

@@ -128,7 +128,7 @@ nk_menu_begin_text(struct nk_context *ctx, const char *title, int len,
     win = ctx->current;
     state = nk_widget(&header, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header,
         title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font))
         is_clicked = nk_true;
@@ -158,7 +158,7 @@ nk_menu_begin_image(struct nk_context *ctx, const char *id, struct nk_image img,
     win = ctx->current;
     state = nk_widget(&header, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header,
         img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in))
         is_clicked = nk_true;
@@ -183,7 +183,7 @@ nk_menu_begin_symbol(struct nk_context *ctx, const char *id,
     win = ctx->current;
     state = nk_widget(&header, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     if (nk_do_button_symbol(&ctx->last_widget_state,  &win->buffer, header,
         sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font))
         is_clicked = nk_true;
@@ -208,7 +208,7 @@ nk_menu_begin_image_text(struct nk_context *ctx, const char *title, int len,
     win = ctx->current;
     state = nk_widget(&header, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer,
         header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button,
         ctx->style.font, in))
@@ -241,7 +241,7 @@ nk_menu_begin_symbol_text(struct nk_context *ctx, const char *title, int len,
     state = nk_widget(&header, ctx);
     if (!state) return 0;
 
-    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer,
         header, sym, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button,
         ctx->style.font, in)) is_clicked = nk_true;

+ 2 - 2
src/nuklear_panel.c

@@ -76,12 +76,12 @@ nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type)
 NK_LIB nk_bool
 nk_panel_is_sub(enum nk_panel_type type)
 {
-    return (type & NK_PANEL_SET_SUB)?1:0;
+    return ((int)type & (int)NK_PANEL_SET_SUB)?1:0;
 }
 NK_LIB nk_bool
 nk_panel_is_nonblock(enum nk_panel_type type)
 {
-    return (type & NK_PANEL_SET_NONBLOCK)?1:0;
+    return ((int)type & (int)NK_PANEL_SET_NONBLOCK)?1:0;
 }
 NK_LIB nk_bool
 nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type)

+ 3 - 3
src/nuklear_popup.c

@@ -27,7 +27,7 @@ nk_popup_begin(struct nk_context *ctx, enum nk_popup_type type,
 
     win = ctx->current;
     panel = win->layout;
-    NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP) && "popups are not allowed to have popups");
+    NK_ASSERT(!((int)panel->type & (int)NK_PANEL_SET_POPUP) && "popups are not allowed to have popups");
     (void)panel;
     title_len = (int)nk_strlen(title);
     title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_POPUP);
@@ -121,7 +121,7 @@ nk_nonblock_begin(struct nk_context *ctx,
     /* popups cannot have popups */
     win = ctx->current;
     panel = win->layout;
-    NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP));
+    NK_ASSERT(!((int)panel->type & (int)NK_PANEL_SET_POPUP));
     (void)panel;
     popup = win->popup.win;
     if (!popup) {
@@ -194,7 +194,7 @@ nk_popup_close(struct nk_context *ctx)
 
     popup = ctx->current;
     NK_ASSERT(popup->parent);
-    NK_ASSERT(popup->layout->type & NK_PANEL_SET_POPUP);
+    NK_ASSERT((int)popup->layout->type & (int)NK_PANEL_SET_POPUP);
     popup->flags |= NK_WINDOW_HIDDEN;
 }
 NK_API void

+ 9 - 9
src/nuklear_progress.c

@@ -62,28 +62,28 @@ nk_draw_progress(struct nk_command_buffer *out, nk_flags state,
     /* draw background */
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
-            nk_draw_image(out, *bounds, &background->data.image, nk_white);
+            nk_draw_image(out, *bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
-            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white);
+            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
-            nk_fill_rect(out, *bounds, style->rounding, background->data.color);
-            nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color);
+            nk_fill_rect(out, *bounds, style->rounding, nk_rgb_factor(background->data.color, style->color_factor));
+            nk_stroke_rect(out, *bounds, style->rounding, style->border, nk_rgb_factor(style->border_color, style->color_factor));
             break;
     }
 
     /* draw cursor */
     switch(cursor->type) {
         case NK_STYLE_ITEM_IMAGE:
-            nk_draw_image(out, *scursor, &cursor->data.image, nk_white);
+            nk_draw_image(out, *scursor, &cursor->data.image, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
-            nk_draw_nine_slice(out, *scursor, &cursor->data.slice, nk_white);
+            nk_draw_nine_slice(out, *scursor, &cursor->data.slice, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
-            nk_fill_rect(out, *scursor, style->rounding, cursor->data.color);
-            nk_stroke_rect(out, *scursor, style->rounding, style->border, style->border_color);
+            nk_fill_rect(out, *scursor, style->rounding, nk_rgb_factor(cursor->data.color, style->color_factor));
+            nk_stroke_rect(out, *scursor, style->rounding, style->border, nk_rgb_factor(style->border_color, style->color_factor));
             break;
     }
 }
@@ -143,7 +143,7 @@ nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, nk_bool is_modify
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
 
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     old_value = *cur;
     *cur = nk_do_progress(&ctx->last_widget_state, &win->buffer, bounds,
             *cur, max, is_modifyable, &style->progress, in);

+ 8 - 6
src/nuklear_property.c

@@ -85,20 +85,22 @@ nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *
         text.text = style->label_normal;
     }
 
+    text.text = nk_rgb_factor(text.text, style->color_factor);
+
     /* draw background */
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_image(out, *bounds, &background->data.image, nk_white);
+            nk_draw_image(out, *bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white);
+            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
             text.background = background->data.color;
-            nk_fill_rect(out, *bounds, style->rounding, background->data.color);
-            nk_stroke_rect(out, *bounds, style->rounding, style->border, background->data.color);
+            nk_fill_rect(out, *bounds, style->rounding, nk_rgb_factor(background->data.color, style->color_factor));
+            nk_stroke_rect(out, *bounds, style->rounding, style->border, nk_rgb_factor(background->data.color, style->color_factor));
             break;
     }
 
@@ -249,7 +251,7 @@ nk_do_property(nk_flags *ws,
     text_edit->string.buffer.memory.ptr = dst;
     text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER;
     text_edit->mode = NK_TEXT_EDIT_MODE_INSERT;
-    nk_do_edit(ws, out, edit, NK_EDIT_FIELD|NK_EDIT_AUTO_SELECT,
+    nk_do_edit(ws, out, edit, (int)NK_EDIT_FIELD|(int)NK_EDIT_AUTO_SELECT,
         filters[filter], text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font);
 
     *length = text_edit->string.len;
@@ -383,7 +385,7 @@ nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant
     old_state = *state;
     ctx->text_edit.clip = ctx->clip;
     in = ((s == NK_WIDGET_ROM && !win->property.active) ||
-        layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+        layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_DISABLED) ? 0 : &ctx->input;
     nk_do_property(&ctx->last_widget_state, &win->buffer, bounds, name,
         variant, inc_per_pixel, buffer, len, state, cursor, select_begin,
         select_end, &style->property, filter, in, style->font, &ctx->text_edit,

+ 9 - 6
src/nuklear_selectable.c

@@ -41,15 +41,18 @@ nk_draw_selectable(struct nk_command_buffer *out,
             text.text = style->text_normal_active;
         }
     }
+
+    text.text = nk_rgb_factor(text.text, style->color_factor);
+
     /* draw selectable background and text */
     switch (background->type) {
         case NK_STYLE_ITEM_IMAGE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_image(out, *bounds, &background->data.image, nk_white);
+            nk_draw_image(out, *bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
             text.background = nk_rgba(0, 0, 0, 0);
-            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white);
+            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
             text.background = background->data.color;
@@ -57,7 +60,7 @@ nk_draw_selectable(struct nk_command_buffer *out,
             break;
     }
     if (icon) {
-        if (img) nk_draw_image(out, *icon, img, nk_white);
+        if (img) nk_draw_image(out, *icon, img, nk_rgb_factor(nk_white, style->color_factor));
         else nk_draw_symbol(out, sym, *icon, text.background, text.text, 1, font);
     }
     nk_widget_text(out, *bounds, string, len, &text, align, font);
@@ -218,7 +221,7 @@ nk_selectable_text(struct nk_context *ctx, const char *str, int len,
 
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds,
                 str, len, align, value, &style->selectable, in, style->font);
 }
@@ -247,7 +250,7 @@ nk_selectable_image_text(struct nk_context *ctx, struct nk_image img,
 
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_selectable_image(&ctx->last_widget_state, &win->buffer, bounds,
                 str, len, align, value, &img, &style->selectable, in, style->font);
 }
@@ -276,7 +279,7 @@ nk_selectable_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym,
 
     state = nk_widget(&bounds, ctx);
     if (!state) return 0;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     return nk_do_selectable_symbol(&ctx->last_widget_state, &win->buffer, bounds,
                 str, len, align, value, sym, &style->selectable, in, style->font);
 }

+ 10 - 9
src/nuklear_slider.c

@@ -78,6 +78,7 @@ nk_draw_slider(struct nk_command_buffer *out, nk_flags state,
         bar_color = style->bar_normal;
         cursor = &style->cursor_normal;
     }
+
     /* calculate slider background bar */
     bar.x = bounds->x;
     bar.y = (visual_cursor->y + visual_cursor->h/2) - bounds->h/12;
@@ -93,26 +94,26 @@ nk_draw_slider(struct nk_command_buffer *out, nk_flags state,
     /* draw background */
     switch(background->type) {
         case NK_STYLE_ITEM_IMAGE:
-            nk_draw_image(out, *bounds, &background->data.image, nk_white);
+            nk_draw_image(out, *bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_NINE_SLICE:
-            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white);
+            nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
             break;
         case NK_STYLE_ITEM_COLOR:
-            nk_fill_rect(out, *bounds, style->rounding, background->data.color);
-            nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color);
+            nk_fill_rect(out, *bounds, style->rounding, nk_rgb_factor(background->data.color, style->color_factor));
+            nk_stroke_rect(out, *bounds, style->rounding, style->border, nk_rgb_factor(style->border_color, style->color_factor));
             break;
     }
 
     /* draw slider bar */
-    nk_fill_rect(out, bar, style->rounding, bar_color);
-    nk_fill_rect(out, fill, style->rounding, style->bar_filled);
+    nk_fill_rect(out, bar, style->rounding, nk_rgb_factor(bar_color, style->color_factor));
+    nk_fill_rect(out, fill, style->rounding, nk_rgb_factor(style->bar_filled, style->color_factor));
 
     /* draw cursor */
     if (cursor->type == NK_STYLE_ITEM_IMAGE)
-        nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_white);
+        nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_rgb_factor(nk_white, style->color_factor));
     else
-        nk_fill_circle(out, *visual_cursor, cursor->data.color);
+        nk_fill_circle(out, *visual_cursor, nk_rgb_factor(cursor->data.color, style->color_factor));
 }
 NK_LIB float
 nk_do_slider(nk_flags *state,
@@ -230,7 +231,7 @@ nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max
 
     state = nk_widget(&bounds, ctx);
     if (!state) return ret;
-    in = (/*state == NK_WIDGET_ROM || */ layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (/*state == NK_WIDGET_ROM || */ state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
 
     old_value = *value;
     *value = nk_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value,

+ 77 - 17
src/nuklear_style.c

@@ -113,27 +113,32 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     text = &style->text;
     text->color = table[NK_COLOR_TEXT];
     text->padding = nk_vec2(0,0);
+    text->color_factor = 1.0f;
+    text->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
 
     /* default button */
     button = &style->button;
     nk_zero_struct(*button);
-    button->normal          = nk_style_item_color(table[NK_COLOR_BUTTON]);
-    button->hover           = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]);
-    button->active          = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]);
-    button->border_color    = table[NK_COLOR_BORDER];
-    button->text_background = table[NK_COLOR_BUTTON];
-    button->text_normal     = table[NK_COLOR_TEXT];
-    button->text_hover      = table[NK_COLOR_TEXT];
-    button->text_active     = table[NK_COLOR_TEXT];
-    button->padding         = nk_vec2(2.0f,2.0f);
-    button->image_padding   = nk_vec2(0.0f,0.0f);
-    button->touch_padding   = nk_vec2(0.0f, 0.0f);
-    button->userdata        = nk_handle_ptr(0);
-    button->text_alignment  = NK_TEXT_CENTERED;
-    button->border          = 1.0f;
-    button->rounding        = 4.0f;
-    button->draw_begin      = 0;
-    button->draw_end        = 0;
+    button->normal                     = nk_style_item_color(table[NK_COLOR_BUTTON]);
+    button->hover                      = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]);
+    button->active                     = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]);
+    button->border_color               = table[NK_COLOR_BORDER];
+    button->text_background            = table[NK_COLOR_BUTTON];
+    button->text_normal                = table[NK_COLOR_TEXT];
+    button->text_hover                 = table[NK_COLOR_TEXT];
+    button->text_active                = table[NK_COLOR_TEXT];
+    button->padding                    = nk_vec2(2.0f,2.0f);
+    button->image_padding              = nk_vec2(0.0f,0.0f);
+    button->touch_padding              = nk_vec2(0.0f, 0.0f);
+    button->userdata                   = nk_handle_ptr(0);
+    button->text_alignment             = NK_TEXT_CENTERED;
+    button->border                     = 1.0f;
+    button->rounding                   = 4.0f;
+    button->color_factor_text          = 1.0f;
+    button->color_factor_background    = 1.0f;
+    button->disabled_factor            = NK_WIDGET_DISABLED_FACTOR;
+    button->draw_begin                 = 0;
+    button->draw_end                   = 0;
 
     /* contextual button */
     button = &style->contextual_button;
@@ -152,6 +157,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 0.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
 
@@ -172,6 +180,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 0.0f;
     button->rounding        = 1.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
 
@@ -193,6 +204,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     toggle->border_color    = nk_rgba(0,0,0,0);
     toggle->border          = 0.0f;
     toggle->spacing         = 4;
+    toggle->color_factor    = 1.0f;
+    toggle->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
 
     /* option toggle */
     toggle = &style->option;
@@ -212,6 +225,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     toggle->border_color    = nk_rgba(0,0,0,0);
     toggle->border          = 0.0f;
     toggle->spacing         = 4;
+    toggle->color_factor    = 1.0f;
+    toggle->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
 
     /* selectable */
     select = &style->selectable;
@@ -233,6 +248,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     select->touch_padding   = nk_vec2(0,0);
     select->userdata        = nk_handle_ptr(0);
     select->rounding        = 0.0f;
+    select->color_factor    = 1.0f;
+    select->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     select->draw_begin      = 0;
     select->draw_end        = 0;
 
@@ -258,6 +275,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     slider->show_buttons    = nk_false;
     slider->bar_height      = 8;
     slider->rounding        = 0;
+    slider->color_factor    = 1.0f;
+    slider->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     slider->draw_begin      = 0;
     slider->draw_end        = 0;
 
@@ -277,6 +296,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 1.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
     style->slider.dec_button = style->slider.inc_button;
@@ -298,6 +320,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     prog->border            = 0;
     prog->cursor_rounding   = 0;
     prog->cursor_border     = 0;
+    prog->color_factor      = 1.0f;
+    prog->disabled_factor   = NK_WIDGET_DISABLED_FACTOR;
     prog->draw_begin        = 0;
     prog->draw_end          = 0;
 
@@ -321,6 +345,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     scroll->rounding        = 0;
     scroll->border_cursor   = 0;
     scroll->rounding_cursor = 0;
+    scroll->color_factor    = 1.0f;
+    scroll->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     scroll->draw_begin      = 0;
     scroll->draw_end        = 0;
     style->scrollv = style->scrollh;
@@ -341,6 +367,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 1.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
     style->scrollh.dec_button = style->scrollh.inc_button;
@@ -372,6 +401,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     edit->cursor_size       = 4;
     edit->border            = 1;
     edit->rounding          = 0;
+    edit->color_factor      = 1.0f;
+    edit->disabled_factor   = NK_WIDGET_DISABLED_FACTOR;
 
     /* property */
     property = &style->property;
@@ -391,6 +422,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     property->rounding      = 10;
     property->draw_begin    = 0;
     property->draw_end      = 0;
+    property->color_factor  = 1.0f;
+    property->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
 
     /* property buttons */
     button = &style->property.dec_button;
@@ -409,6 +442,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 0.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
     style->property.inc_button = style->property.dec_button;
@@ -435,6 +471,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     edit->cursor_size       = 8;
     edit->border            = 0;
     edit->rounding          = 0;
+    edit->color_factor      = 1.0f;
+    edit->disabled_factor   = NK_WIDGET_DISABLED_FACTOR;
 
     /* chart */
     chart = &style->chart;
@@ -446,6 +484,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     chart->padding          = nk_vec2(4,4);
     chart->border           = 0;
     chart->rounding         = 0;
+    chart->color_factor     = 1.0f;
+    chart->disabled_factor  = NK_WIDGET_DISABLED_FACTOR;
+    chart->show_markers     = nk_true;
 
     /* combo */
     combo = &style->combo;
@@ -464,6 +505,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     combo->spacing          = nk_vec2(4,0);
     combo->border           = 1;
     combo->rounding         = 0;
+    combo->color_factor     = 1.0f;
+    combo->disabled_factor  = NK_WIDGET_DISABLED_FACTOR;
 
     /* combo button */
     button = &style->combo.button;
@@ -482,6 +525,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 0.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
 
@@ -497,6 +543,8 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     tab->indent             = 10.0f;
     tab->border             = 1;
     tab->rounding           = 0;
+    tab->color_factor       = 1.0f;
+    tab->disabled_factor    = NK_WIDGET_DISABLED_FACTOR;
 
     /* tab button */
     button = &style->tab.tab_minimize_button;
@@ -515,6 +563,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 0.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
     style->tab.tab_maximize_button =*button;
@@ -536,6 +587,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 0.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
     style->tab.node_maximize_button =*button;
@@ -573,6 +627,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 0.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
 
@@ -593,6 +650,9 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
     button->text_alignment  = NK_TEXT_CENTERED;
     button->border          = 0.0f;
     button->rounding        = 0.0f;
+    button->color_factor_text    = 1.0f;
+    button->color_factor_background = 1.0f;
+    button->disabled_factor = NK_WIDGET_DISABLED_FACTOR;
     button->draw_begin      = 0;
     button->draw_end        = 0;
 

+ 2 - 2
src/nuklear_text.c

@@ -114,7 +114,7 @@ nk_text_colored(struct nk_context *ctx, const char *str, int len,
     text.padding.x = item_padding.x;
     text.padding.y = item_padding.y;
     text.background = style->window.background;
-    text.text = color;
+    text.text = nk_rgb_factor(color, style->text.color_factor);
     nk_widget_text(&win->buffer, bounds, str, len, &text, alignment, style->font);
 }
 NK_API void
@@ -141,7 +141,7 @@ nk_text_wrap_colored(struct nk_context *ctx, const char *str,
     text.padding.x = item_padding.x;
     text.padding.y = item_padding.y;
     text.background = style->window.background;
-    text.text = color;
+    text.text = nk_rgb_factor(color, style->text.color_factor);
     nk_widget_text_wrap(&win->buffer, bounds, str, len, &text, style->font);
 }
 #ifdef NK_INCLUDE_STANDARD_VARARGS

+ 154 - 35
src/nuklear_toggle.c

@@ -26,7 +26,7 @@ nk_draw_checkbox(struct nk_command_buffer *out,
     nk_flags state, const struct nk_style_toggle *style, nk_bool active,
     const struct nk_rect *label, const struct nk_rect *selector,
     const struct nk_rect *cursors, const char *string, int len,
-    const struct nk_user_font *font)
+    const struct nk_user_font *font, nk_flags text_alignment)
 {
     const struct nk_style_item *background;
     const struct nk_style_item *cursor;
@@ -47,28 +47,29 @@ nk_draw_checkbox(struct nk_command_buffer *out,
         text.text = style->text_normal;
     }
 
+    text.text = nk_rgb_factor(text.text, style->color_factor);
+    text.padding.x = 0;
+    text.padding.y = 0;
+    text.background = style->text_background;
+    nk_widget_text(out, *label, string, len, &text, text_alignment, font);
+
     /* draw background and cursor */
     if (background->type == NK_STYLE_ITEM_COLOR) {
-        nk_fill_rect(out, *selector, 0, style->border_color);
-        nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, background->data.color);
-    } else nk_draw_image(out, *selector, &background->data.image, nk_white);
+        nk_fill_rect(out, *selector, 0, nk_rgb_factor(style->border_color, style->color_factor));
+        nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, nk_rgb_factor(background->data.color, style->color_factor));
+    } else nk_draw_image(out, *selector, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
     if (active) {
         if (cursor->type == NK_STYLE_ITEM_IMAGE)
-            nk_draw_image(out, *cursors, &cursor->data.image, nk_white);
+            nk_draw_image(out, *cursors, &cursor->data.image, nk_rgb_factor(nk_white, style->color_factor));
         else nk_fill_rect(out, *cursors, 0, cursor->data.color);
     }
-
-    text.padding.x = 0;
-    text.padding.y = 0;
-    text.background = style->text_background;
-    nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font);
 }
 NK_LIB void
 nk_draw_option(struct nk_command_buffer *out,
     nk_flags state, const struct nk_style_toggle *style, nk_bool active,
     const struct nk_rect *label, const struct nk_rect *selector,
     const struct nk_rect *cursors, const char *string, int len,
-    const struct nk_user_font *font)
+    const struct nk_user_font *font, nk_flags text_alignment)
 {
     const struct nk_style_item *background;
     const struct nk_style_item *cursor;
@@ -89,28 +90,29 @@ nk_draw_option(struct nk_command_buffer *out,
         text.text = style->text_normal;
     }
 
+    text.text = nk_rgb_factor(text.text, style->color_factor);
+    text.padding.x = 0;
+    text.padding.y = 0;
+    text.background = style->text_background;
+    nk_widget_text(out, *label, string, len, &text, text_alignment, font);
+
     /* draw background and cursor */
     if (background->type == NK_STYLE_ITEM_COLOR) {
-        nk_fill_circle(out, *selector, style->border_color);
-        nk_fill_circle(out, nk_shrink_rect(*selector, style->border), background->data.color);
-    } else nk_draw_image(out, *selector, &background->data.image, nk_white);
+        nk_fill_circle(out, *selector, nk_rgb_factor(style->border_color, style->color_factor));
+        nk_fill_circle(out, nk_shrink_rect(*selector, style->border), nk_rgb_factor(background->data.color, style->color_factor));
+    } else nk_draw_image(out, *selector, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
     if (active) {
         if (cursor->type == NK_STYLE_ITEM_IMAGE)
-            nk_draw_image(out, *cursors, &cursor->data.image, nk_white);
+            nk_draw_image(out, *cursors, &cursor->data.image, nk_rgb_factor(nk_white, style->color_factor));
         else nk_fill_circle(out, *cursors, cursor->data.color);
     }
-
-    text.padding.x = 0;
-    text.padding.y = 0;
-    text.background = style->text_background;
-    nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font);
 }
 NK_LIB nk_bool
 nk_do_toggle(nk_flags *state,
     struct nk_command_buffer *out, struct nk_rect r,
     nk_bool *active, const char *str, int len, enum nk_toggle_type type,
     const struct nk_style_toggle *style, const struct nk_input *in,
-    const struct nk_user_font *font)
+    const struct nk_user_font *font, nk_flags widget_alignment, nk_flags text_alignment)
 {
     int was_active;
     struct nk_rect bounds;
@@ -136,8 +138,37 @@ nk_do_toggle(nk_flags *state,
     /* calculate the selector space */
     select.w = font->height;
     select.h = select.w;
-    select.y = r.y + r.h/2.0f - select.h/2.0f;
-    select.x = r.x;
+
+    if (widget_alignment & NK_WIDGET_ALIGN_RIGHT) {
+        select.x = r.x + r.w - font->height;
+
+        /* label in front of the selector */
+        label.x = r.x;
+        label.w = r.w - select.w - style->spacing * 2;
+    } else if (widget_alignment & NK_WIDGET_ALIGN_CENTERED) {
+        select.x = r.x + (r.w - select.w) / 2;
+
+        /* label in front of selector */
+        label.x = r.x;
+        label.w = (r.w - select.w - style->spacing * 2) / 2;
+    } else { /* Default: NK_WIDGET_ALIGN_LEFT */
+        select.x = r.x;
+
+        /* label behind the selector */
+        label.x = select.x + select.w + style->spacing;
+        label.w = NK_MAX(r.x + r.w, label.x) - label.x;
+    }
+
+    if (widget_alignment & NK_WIDGET_ALIGN_TOP) {
+        select.y = r.y;
+    } else if (widget_alignment & NK_WIDGET_ALIGN_BOTTOM) {
+        select.y = r.y + r.h - select.h - 2 * style->padding.y;
+    } else { /* Default: NK_WIDGET_ALIGN_MIDDLE */
+        select.y = r.y + r.h/2.0f - select.h/2.0f;
+    }
+
+    label.y = select.y;
+    label.h = select.w;
 
     /* calculate the bounds of the cursor inside the selector */
     cursor.x = select.x + style->padding.x + style->border;
@@ -145,12 +176,6 @@ nk_do_toggle(nk_flags *state,
     cursor.w = select.w - (2 * style->padding.x + 2 * style->border);
     cursor.h = select.h - (2 * style->padding.y + 2 * style->border);
 
-    /* label behind the selector */
-    label.x = select.x + select.w + style->spacing;
-    label.y = select.y;
-    label.w = NK_MAX(r.x + r.w, label.x) - label.x;
-    label.h = select.w;
-
     /* update selector */
     was_active = *active;
     *active = nk_toggle_behavior(in, bounds, state, *active);
@@ -159,9 +184,9 @@ nk_do_toggle(nk_flags *state,
     if (style->draw_begin)
         style->draw_begin(out, style->userdata);
     if (type == NK_TOGGLE_CHECK) {
-        nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font);
+        nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font, text_alignment);
     } else {
-        nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font);
+        nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font, text_alignment);
     }
     if (style->draw_end)
         style->draw_end(out, style->userdata);
@@ -195,9 +220,37 @@ nk_check_text(struct nk_context *ctx, const char *text, int len, nk_bool active)
 
     state = nk_widget(&bounds, ctx);
     if (!state) return active;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active,
+        text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font, NK_WIDGET_LEFT, NK_TEXT_LEFT);
+    return active;
+}
+NK_API nk_bool
+nk_check_text_align(struct nk_context *ctx, const char *text, int len, nk_bool active, nk_flags widget_alignment, nk_flags text_alignment)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return active;
+
+    win = ctx->current;
+    style = &ctx->style;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return active;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active,
-        text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font);
+        text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font, widget_alignment, text_alignment);
     return active;
 }
 NK_API unsigned int
@@ -227,6 +280,18 @@ nk_checkbox_text(struct nk_context *ctx, const char *text, int len, nk_bool *act
     return old_val != *active;
 }
 NK_API nk_bool
+nk_checkbox_text_align(struct nk_context *ctx, const char *text, int len, nk_bool *active, nk_flags widget_alignment, nk_flags text_alignment)
+{
+    int old_val;
+    NK_ASSERT(ctx);
+    NK_ASSERT(text);
+    NK_ASSERT(active);
+    if (!ctx || !text || !active) return 0;
+    old_val = *active;
+    *active = nk_check_text_align(ctx, text, len, *active, widget_alignment, text_alignment);
+    return old_val != *active;
+}
+NK_API nk_bool
 nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len,
     unsigned int *flags, unsigned int value)
 {
@@ -257,6 +322,10 @@ NK_API nk_bool nk_checkbox_label(struct nk_context *ctx, const char *label, nk_b
 {
     return nk_checkbox_text(ctx, label, nk_strlen(label), active);
 }
+NK_API nk_bool nk_checkbox_label_align(struct nk_context *ctx, const char *label, nk_bool *active, nk_flags widget_alignment, nk_flags text_alignment)
+{
+    return nk_checkbox_text_align(ctx, label, nk_strlen(label), active, widget_alignment, text_alignment);
+}
 NK_API nk_bool nk_checkbox_flags_label(struct nk_context *ctx, const char *label,
     unsigned int *flags, unsigned int value)
 {
@@ -290,9 +359,37 @@ nk_option_text(struct nk_context *ctx, const char *text, int len, nk_bool is_act
 
     state = nk_widget(&bounds, ctx);
     if (!state) return (int)state;
-    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
     nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active,
-        text, len, NK_TOGGLE_OPTION, &style->option, in, style->font);
+        text, len, NK_TOGGLE_OPTION, &style->option, in, style->font, NK_WIDGET_LEFT, NK_TEXT_LEFT);
+    return is_active;
+}
+NK_API nk_bool
+nk_option_text_align(struct nk_context *ctx, const char *text, int len, nk_bool is_active, nk_flags widget_alignment, nk_flags text_alignment)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return is_active;
+
+    win = ctx->current;
+    style = &ctx->style;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return (int)state;
+    in = (state == NK_WIDGET_ROM || state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active,
+        text, len, NK_TOGGLE_OPTION, &style->option, in, style->font, widget_alignment, text_alignment);
     return is_active;
 }
 NK_API nk_bool
@@ -308,13 +405,35 @@ nk_radio_text(struct nk_context *ctx, const char *text, int len, nk_bool *active
     return old_value != *active;
 }
 NK_API nk_bool
+nk_radio_text_align(struct nk_context *ctx, const char *text, int len, nk_bool *active, nk_flags widget_alignment, nk_flags text_alignment)
+{
+    int old_value;
+    NK_ASSERT(ctx);
+    NK_ASSERT(text);
+    NK_ASSERT(active);
+    if (!ctx || !text || !active) return 0;
+    old_value = *active;
+    *active = nk_option_text_align(ctx, text, len, old_value, widget_alignment, text_alignment);
+    return old_value != *active;
+}
+NK_API nk_bool
 nk_option_label(struct nk_context *ctx, const char *label, nk_bool active)
 {
     return nk_option_text(ctx, label, nk_strlen(label), active);
 }
 NK_API nk_bool
+nk_option_label_align(struct nk_context *ctx, const char *label, nk_bool active, nk_flags widget_alignment, nk_flags text_alignment)
+{
+    return nk_option_text_align(ctx, label, nk_strlen(label), active, widget_alignment, text_alignment);
+}
+NK_API nk_bool
 nk_radio_label(struct nk_context *ctx, const char *label, nk_bool *active)
 {
     return nk_radio_text(ctx, label, nk_strlen(label), active);
 }
+NK_API nk_bool
+nk_radio_label_align(struct nk_context *ctx, const char *label, nk_bool *active, nk_flags widget_alignment, nk_flags text_alignment)
+{
+    return nk_radio_text_align(ctx, label, nk_strlen(label), active, widget_alignment, text_alignment);
+}
 

+ 1 - 1
src/nuklear_tooltip.c

@@ -24,7 +24,7 @@ nk_tooltip_begin(struct nk_context *ctx, float width)
     /* make sure that no nonblocking popup is currently active */
     win = ctx->current;
     in = &ctx->input;
-    if (win->popup.win && (win->popup.type & NK_PANEL_SET_NONBLOCK))
+    if (win->popup.win && ((int)win->popup.type & (int)NK_PANEL_SET_NONBLOCK))
         return 0;
 
     w = nk_iceilf(width);

+ 10 - 9
src/nuklear_tree.c

@@ -52,15 +52,15 @@ nk_tree_state_base(struct nk_context *ctx, enum nk_tree_type type,
 
         switch(background->type) {
             case NK_STYLE_ITEM_IMAGE:
-                nk_draw_image(out, header, &background->data.image, nk_white);
+                nk_draw_image(out, header, &background->data.image, nk_rgb_factor(nk_white, style->tab.color_factor));
                 break;
             case NK_STYLE_ITEM_NINE_SLICE:
-                nk_draw_nine_slice(out, header, &background->data.slice, nk_white);
+                nk_draw_nine_slice(out, header, &background->data.slice, nk_rgb_factor(nk_white, style->tab.color_factor));
                 break;
             case NK_STYLE_ITEM_COLOR:
-                nk_fill_rect(out, header, 0, style->tab.border_color);
+                nk_fill_rect(out, header, 0, nk_rgb_factor(style->tab.border_color, style->tab.color_factor));
                 nk_fill_rect(out, nk_shrink_rect(header, style->tab.border),
-                    style->tab.rounding, background->data.color);
+                    style->tab.rounding, nk_rgb_factor(background->data.color, style->tab.color_factor));
                 break;
         }
     } else text.background = style->window.background;
@@ -105,7 +105,7 @@ nk_tree_state_base(struct nk_context *ctx, enum nk_tree_type type,
     label.y = sym.y;
     label.w = header.w - (sym.w + item_spacing.y + style->tab.indent);
     label.h = style->font->height;
-    text.text = style->tab.text;
+    text.text = nk_rgb_factor(style->tab.text, style->tab.color_factor);
     text.padding = nk_vec2(0,0);
     nk_widget_text(out, label, title, nk_strlen(title), &text,
         NK_TEXT_LEFT, style->font);}
@@ -242,15 +242,16 @@ nk_tree_element_image_push_hashed_base(struct nk_context *ctx, enum nk_tree_type
 
         switch (background->type) {
             case NK_STYLE_ITEM_IMAGE:
-                nk_draw_image(out, header, &background->data.image, nk_white);
+                nk_draw_image(out, header, &background->data.image, nk_rgb_factor(nk_white, style->tab.color_factor));
                 break;
             case NK_STYLE_ITEM_NINE_SLICE:
-                nk_draw_nine_slice(out, header, &background->data.slice, nk_white);
+                nk_draw_nine_slice(out, header, &background->data.slice, nk_rgb_factor(nk_white, style->tab.color_factor));
                 break;
             case NK_STYLE_ITEM_COLOR:
-                nk_fill_rect(out, header, 0, style->tab.border_color);
+                nk_fill_rect(out, header, 0, nk_rgb_factor(style->tab.border_color, style->tab.color_factor));
                 nk_fill_rect(out, nk_shrink_rect(header, style->tab.border),
-                    style->tab.rounding, background->data.color);
+                    style->tab.rounding, nk_rgb_factor(background->data.color, style->tab.color_factor));
+
                 break;
         }
     }

+ 127 - 0
src/nuklear_widget.c

@@ -175,6 +175,8 @@ nk_widget(struct nk_rect *bounds, const struct nk_context *ctx)
     nk_unify(&v, &c, bounds->x, bounds->y, bounds->x + bounds->w, bounds->y + bounds->h);
     if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds->x, bounds->y, bounds->w, bounds->h))
         return NK_WIDGET_INVALID;
+    if (win->widgets_disabled)
+        return NK_WIDGET_DISABLED;
     if (!NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, v.x, v.y, v.w, v.h))
         return NK_WIDGET_ROM;
     return NK_WIDGET_VALID;
@@ -227,4 +229,129 @@ nk_spacing(struct nk_context *ctx, int cols)
             nk_panel_alloc_space(&none, ctx);
     } layout->row.index = index;
 }
+NK_API void
+nk_widget_disable_begin(struct nk_context* ctx)
+{
+    struct nk_window* win;
+    struct nk_style* style;
 
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+
+    if (!ctx || !ctx->current)
+        return;
+
+    win = ctx->current;
+    style = &ctx->style;
+
+    win->widgets_disabled = nk_true;
+
+    style->button.color_factor_text = style->button.disabled_factor;
+    style->button.color_factor_background = style->button.disabled_factor;
+    style->chart.color_factor = style->chart.disabled_factor;
+    style->checkbox.color_factor = style->checkbox.disabled_factor;
+    style->combo.color_factor = style->combo.disabled_factor;
+    style->combo.button.color_factor_text = style->combo.button.disabled_factor;
+    style->combo.button.color_factor_background = style->combo.button.disabled_factor;
+    style->contextual_button.color_factor_text = style->contextual_button.disabled_factor;
+    style->contextual_button.color_factor_background = style->contextual_button.disabled_factor;
+    style->edit.color_factor = style->edit.disabled_factor;
+    style->edit.scrollbar.color_factor = style->edit.scrollbar.disabled_factor;
+    style->menu_button.color_factor_text = style->menu_button.disabled_factor;
+    style->menu_button.color_factor_background = style->menu_button.disabled_factor;
+    style->option.color_factor = style->option.disabled_factor;
+    style->progress.color_factor = style->progress.disabled_factor;
+    style->property.color_factor = style->property.disabled_factor;
+    style->property.inc_button.color_factor_text = style->property.inc_button.disabled_factor;
+    style->property.inc_button.color_factor_background = style->property.inc_button.disabled_factor;
+    style->property.dec_button.color_factor_text = style->property.dec_button.disabled_factor;
+    style->property.dec_button.color_factor_background = style->property.dec_button.disabled_factor;
+    style->property.edit.color_factor = style->property.edit.disabled_factor;
+    style->scrollh.color_factor = style->scrollh.disabled_factor;
+    style->scrollh.inc_button.color_factor_text = style->scrollh.inc_button.disabled_factor;
+    style->scrollh.inc_button.color_factor_background = style->scrollh.inc_button.disabled_factor;
+    style->scrollh.dec_button.color_factor_text = style->scrollh.dec_button.disabled_factor;
+    style->scrollh.dec_button.color_factor_background = style->scrollh.dec_button.disabled_factor;
+    style->scrollv.color_factor = style->scrollv.disabled_factor;
+    style->scrollv.inc_button.color_factor_text = style->scrollv.inc_button.disabled_factor;
+    style->scrollv.inc_button.color_factor_background = style->scrollv.inc_button.disabled_factor;
+    style->scrollv.dec_button.color_factor_text = style->scrollv.dec_button.disabled_factor;
+    style->scrollv.dec_button.color_factor_background = style->scrollv.dec_button.disabled_factor;
+    style->selectable.color_factor = style->selectable.disabled_factor;
+    style->slider.color_factor = style->slider.disabled_factor;
+    style->slider.inc_button.color_factor_text = style->slider.inc_button.disabled_factor;
+    style->slider.inc_button.color_factor_background = style->slider.inc_button.disabled_factor;
+    style->slider.dec_button.color_factor_text = style->slider.dec_button.disabled_factor;
+    style->slider.dec_button.color_factor_background = style->slider.dec_button.disabled_factor;
+    style->tab.color_factor = style->tab.disabled_factor;
+    style->tab.node_maximize_button.color_factor_text = style->tab.node_maximize_button.disabled_factor;
+    style->tab.node_minimize_button.color_factor_text = style->tab.node_minimize_button.disabled_factor;
+    style->tab.tab_maximize_button.color_factor_text = style->tab.tab_maximize_button.disabled_factor;
+    style->tab.tab_maximize_button.color_factor_background = style->tab.tab_maximize_button.disabled_factor;
+    style->tab.tab_minimize_button.color_factor_text = style->tab.tab_minimize_button.disabled_factor;
+    style->tab.tab_minimize_button.color_factor_background = style->tab.tab_minimize_button.disabled_factor;
+    style->text.color_factor = style->text.disabled_factor;
+}
+NK_API void
+nk_widget_disable_end(struct nk_context* ctx)
+{
+    struct nk_window* win;
+    struct nk_style* style;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+
+    if (!ctx || !ctx->current)
+        return;
+
+    win = ctx->current;
+    style = &ctx->style;
+
+    win->widgets_disabled = nk_false;
+
+    style->button.color_factor_text = 1.0f;
+    style->button.color_factor_background = 1.0f;
+    style->chart.color_factor = 1.0f;
+    style->checkbox.color_factor = 1.0f;
+    style->combo.color_factor = 1.0f;
+    style->combo.button.color_factor_text = 1.0f;
+    style->combo.button.color_factor_background = 1.0f;
+    style->contextual_button.color_factor_text = 1.0f;
+    style->contextual_button.color_factor_background = 1.0f;
+    style->edit.color_factor = 1.0f;
+    style->edit.scrollbar.color_factor = 1.0f;
+    style->menu_button.color_factor_text = 1.0f;
+    style->menu_button.color_factor_background = 1.0f;
+    style->option.color_factor = 1.0f;
+    style->progress.color_factor = 1.0f;
+    style->property.color_factor = 1.0f;
+    style->property.inc_button.color_factor_text = 1.0f;
+    style->property.inc_button.color_factor_background = 1.0f;
+    style->property.dec_button.color_factor_text = 1.0f;
+    style->property.dec_button.color_factor_background = 1.0f;
+    style->property.edit.color_factor = 1.0f;
+    style->scrollh.color_factor = 1.0f;
+    style->scrollh.inc_button.color_factor_text = 1.0f;
+    style->scrollh.inc_button.color_factor_background = 1.0f;
+    style->scrollh.dec_button.color_factor_text = 1.0f;
+    style->scrollh.dec_button.color_factor_background = 1.0f;
+    style->scrollv.color_factor = 1.0f;
+    style->scrollv.inc_button.color_factor_text = 1.0f;
+    style->scrollv.inc_button.color_factor_background = 1.0f;
+    style->scrollv.dec_button.color_factor_text = 1.0f;
+    style->scrollv.dec_button.color_factor_background = 1.0f;
+    style->selectable.color_factor = 1.0f;
+    style->slider.color_factor = 1.0f;
+    style->slider.inc_button.color_factor_text = 1.0f;
+    style->slider.inc_button.color_factor_background = 1.0f;
+    style->slider.dec_button.color_factor_text = 1.0f;
+    style->slider.dec_button.color_factor_background = 1.0f;
+    style->tab.color_factor = 1.0f;
+    style->tab.node_maximize_button.color_factor_text = 1.0f;
+    style->tab.node_minimize_button.color_factor_text = 1.0f;
+    style->tab.tab_maximize_button.color_factor_text = 1.0f;
+    style->tab.tab_maximize_button.color_factor_background = 1.0f;
+    style->tab.tab_minimize_button.color_factor_text = 1.0f;
+    style->tab.tab_minimize_button.color_factor_background = 1.0f;
+    style->text.color_factor = 1.0f;
+}

+ 1 - 0
src/nuklear_window.c

@@ -180,6 +180,7 @@ nk_begin_titled(struct nk_context *ctx, const char *name, const char *title,
         NK_MEMCPY(win->name_string, name, name_length);
         win->name_string[name_length] = 0;
         win->popup.win = 0;
+        win->widgets_disabled = nk_false;
         if (!ctx->active)
             ctx->active = win;
     } else {

+ 2 - 2
src/stb_rect_pack.h

@@ -311,7 +311,7 @@ static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0
       if (node->y > min_y) {
          // raise min_y higher.
          // we've accounted for all waste up to min_y,
-         // but we'll now add more waste for everything we've visted
+         // but we'll now add more waste for everything we've visited
          waste_area += visited_width * (node->y - min_y);
          min_y = node->y;
          // the first time through, visited_width might be reduced
@@ -466,7 +466,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
 
    // insert the new node into the right starting point, and
    // let 'cur' point to the remaining nodes needing to be
-   // stiched back in
+   // stitched back in
 
    cur = *res.prev_link;
    if (cur->x < res.x) {

Некоторые файлы не были показаны из-за большого количества измененных файлов