vurtun / nuklear

A single-header ANSI C gui library
13.68k stars 1.11k forks source link

Added loop detection to the context draw command list #895

Closed Lory171 closed 4 years ago

Lory171 commented 5 years ago

I found out that sometimes the contextual menus can freeze nuklear, this happen with both nk_contextual_xxx and nk_menu_xxx. Searching for the origin of the freeze I found out it's a loop inside the context draw command list: one command in the list reference a previous already processed one, causing a loop. I added a simple loop detection and suppression to nk__begin and nk__next (the functions that should be used on the command list), that way any loop is quickly detected and fixed. I did not investigate why this happen with contextual menus to begin with because I do not have time, but I can implement a debug function to get informations about a detected loop if needed.

dumblob commented 5 years ago

Could you please post your source code to let us check whether there is anything suspicious in how the library is used? One of the goals of nuklear.h is to provide a way to avoid such potential mistakes (e.g. by issuing a warning in the console if the loop was detected).

I'll leave this pull open until we're sure it's not an issue with the usage of the library. I'll then consider merging it and will ask you to open an issue to not forget about finding the real culprit and adding a comment in this pull request pointing out, that the real issue has not been found as of 20190829.

Lory171 commented 5 years ago

Sharing my actual code it's not possible, mainly because it's too long. I made for you a test case that trigger the issue.


static void main_gui(struct nk_context *ctx){
    if(nk_begin(ctx, "GUI", nk_rect(0, 0, 1024, 768), NK_WINDOW_NO_SCROLLBAR)){
        nk_menubar_begin(ctx);
        nk_layout_row_static(ctx, 0, 70, 3);
        if(nk_menu_begin_label(ctx, "FILE", NK_TEXT_LEFT, nk_vec2(100, 300))){
            nk_layout_row_dynamic(ctx, 0, 1);
            if(nk_menu_item_label(ctx, "New", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Open", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Save", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Save as..", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Close", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Reload", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Exit", NK_TEXT_LEFT)){}
            nk_menu_end(ctx);
        } if(nk_menu_begin_label(ctx, "EDIT", NK_TEXT_LEFT, nk_vec2(80, 300))){
            nk_layout_row_dynamic(ctx, 0, 1);
            if(nk_menu_item_label(ctx, "New...", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Edit", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Delete", NK_TEXT_LEFT)){}
            nk_menu_end(ctx);
        } if(nk_menu_begin_label(ctx, "ABOUT", NK_TEXT_LEFT, nk_vec2(100, 300))){
            nk_layout_row_dynamic(ctx, 0, 1);
            if(nk_menu_item_label(ctx, "Info", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Help", NK_TEXT_LEFT)){}
            if(nk_menu_item_label(ctx, "Version", NK_TEXT_LEFT)){}
            nk_menu_end(ctx);
        } nk_menubar_end(ctx);
        nk_layout_row_dynamic(ctx, 650, 1);
        if(nk_group_begin(ctx, "EDITOR", 0)){
            for(int i = 0; i<5; i++){
                if(nk_tree_push_id(ctx, NK_TREE_NODE, "Tree", NK_MAXIMIZED, i)){
                    for(int j = 0; j<10; j++){
                        if(nk_tree_push_id(ctx, NK_TREE_NODE, "Node", NK_MAXIMIZED, j)){
                            nk_label(ctx, "Content", NK_TEXT_LEFT);
                            nk_tree_pop(ctx);
                        }
                    } nk_tree_pop(ctx);
                }
            } nk_group_end(ctx);
        }
    } nk_end(ctx);
    return;
}

Just add the gui code to your render, when you open the first menu it should freeze. With my pull request the loop is skipped and the gui keeps running.