Search code examples
clinuxcurses

Segmentation fault when trying to delete window


I only need the WINDOW for the animation at the start and main_menu but after I get thru the if statement in main_menu that would delete win, I get segmentation fault and I just can't get rid of it. I want to delete it because when I try to clear it, the border and "Sneak" disappear, but "Play, Rules, Credits and Quit" stay on screen. Any idea on how to delete it properly?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>
#include <time.h>
#include <unistd.h>

void main_menu(int xm, int ym);
void rules(int xm, int ym);

int main(){
    initscr();
    noecho();
    curs_set(0);

    int xm, ym;
    getmaxyx(stdscr, ym, xm);
    WINDOW *win = newwin(ym/2, xm/2, ym/4, xm/4);
    
    box(win, 0, 0);
    mvwprintw(win, ym/4 - 3,  xm/4 - 2, "Play");
    mvwprintw(win, ym/4,  xm/4 - 4, "Options");
    mvwprintw(win, ym/4 + 3,  xm/4 - 2, "Quit");
    wrefresh(win);

    int anim = 2;
    int intro = 3;
    clock_t start_time;
    while(1){
        wclear(win);
        box(win, 0, 0);
        mvwprintw(win, 0, anim, "Snake");

        mvwprintw(win, ym/4, xm/4 - 5, "Loading...");
        wrefresh(win);

        start_time = clock();
        while(clock() < start_time + 30000){}

        if(intro == 3){
            if(anim < xm/2 - 7) anim++;
            else intro--;
        }
        if(intro == 2){
            if(anim > 2) anim--;
            else intro--;
        }
        if(intro == 1){
            if(anim < xm/4 - 2) anim++;
            else intro--;
        }
        if(intro == 0) break;
    }

    wborder(win, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
    wrefresh(win);
    delwin(win);
    main_menu(xm, ym);

    endwin();
    return 0;
}

void main_menu(int xm, int ym){
    WINDOW *win = newwin(ym/2, xm/2, ym/4, xm/4);
    wclear(win);
    box(win, 0, 0);
    
    mvwprintw(win, 0, xm/4 - 2, "Snake");
    wrefresh(win);

    keypad(win, TRUE);
    int selected = 1;
    int slider = 0;

    wattron(win, A_STANDOUT);
    mvwprintw(win, ym/4 - 4,  xm/4 - 2, "Play");
    wattroff(win, A_STANDOUT);
    mvwprintw(win, ym/4 - 1,  xm/4 - 3, "Rules");
    mvwprintw(win, ym/4 + 2,  xm/4 - 3, "Credits");
    mvwprintw(win, ym/4 + 5,  xm/4 - 2, "Quit");
    while(1){
        slider = wgetch(win);
        
        if(slider == KEY_UP){
            if(selected != 1) selected--;
        }

        if(slider == KEY_DOWN){
            if(selected != 4) selected++;
        }

        if(slider == 10){
            wborder(win, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
            wrefresh(win);
            delwin(win);
            if(selected == 2) rules(xm, ym);
        }

        switch(selected){
            case 1:
                wattron(win, A_STANDOUT);
                mvwprintw(win, ym/4 - 4,  xm/4 - 2, "Play");
                wattroff(win, A_STANDOUT);
                mvwprintw(win, ym/4 - 1,  xm/4 - 3, "Rules");
                mvwprintw(win, ym/4 + 2,  xm/4 - 3, "Credits");
                mvwprintw(win, ym/4 + 5,  xm/4 - 2, "Quit");
                break;
            case 2:
                wattron(win, A_STANDOUT);
                mvwprintw(win, ym/4 - 1,  xm/4 - 3, "Rules");
                wattroff(win, A_STANDOUT);
                mvwprintw(win, ym/4 - 4,  xm/4 - 2, "Play");
                mvwprintw(win, ym/4 + 2,  xm/4 - 3, "Credits");
                mvwprintw(win, ym/4 + 5,  xm/4 - 2, "Quit");
                break;
            case 3:
                wattron(win, A_STANDOUT);
                mvwprintw(win, ym/4 + 2,  xm/4 - 3, "Credits");
                wattroff(win, A_STANDOUT);
                mvwprintw(win, ym/4 - 4,  xm/4 - 2, "Play");
                mvwprintw(win, ym/4 - 1,  xm/4 - 3, "Rules");
                mvwprintw(win, ym/4 + 5,  xm/4 - 2, "Quit");
                break;
            case 4:
                wattron(win, A_STANDOUT);
                mvwprintw(win, ym/4 + 5,  xm/4 - 2, "Quit");
                wattroff(win, A_STANDOUT);
                mvwprintw(win, ym/4 - 4,  xm/4 - 2, "Play");
                mvwprintw(win, ym/4 - 1,  xm/4 - 3, "Rules");
                mvwprintw(win, ym/4 + 2,  xm/4 - 3, "Credits");
                break;
            default:
                mvwprintw(win, ym/4 - 4,  xm/4 - 2, "Play");
                mvwprintw(win, ym/4 - 1,  xm/4 - 3, "Rules");
                mvwprintw(win, ym/4 + 2,  xm/4 - 3, "Credits");
                mvwprintw(win, ym/4 + 3,  xm/4 - 2, "Quit");
                break;
        }

        wrefresh(win);
    }
}

void rules(int xm, int ym){
    clear();
    box(win, 0, 0);

    

    //   ─│╲╱
    refresh();
}

I would prefer to delete the window rather than clearing it, since I will only use it for main_menu() and maybe the main game.


Solution

  • The window is deleted whenever the user presses the ENTER/RETURN key

    if (slider == 10) {
        /* this section looks copy-pasted from `main` */
        wborder(win, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
        wrefresh(win);
        delwin(win); /* <----------- */
    
        if (selected == 2)
            rules(xm, ym);
    }
    

    Even if selected is 2, rules will eventually return back to main_menu and execution will continue.

    In any case, win is always used in the switch statement:

    switch (selected) {
        case 1:
            wattron(win, A_STANDOUT);
            /* ... */
    

    This means you are operating on a dangling pointer.


    For something straightforward, you may find it easier to manage each "scene" if they are clearly separate from one another.

    Here's a cursory refactoring / outline:

    #include <curses.h>
    #include <string.h>
    
    enum MENU_OPTIONS { MENU_PLAY, MENU_RULES, MENU_CREDITS, MENU_QUIT, MENU_LENGTH };
    static const char *menu_entries[] = { "Play", "Rules", "Credits", "Quit" };
    
    void print_list(WINDOW *w, const char **items, int length, int highlight,
            int y_origin, int x_origin, int spacing)
    {
        for (int i = 0; i < length; i++) {
            if (highlight == i)
                wattron(w, A_STANDOUT);
    
            mvwprintw(w,
                y_origin - (spacing * length / 2) + (i * spacing),
                x_origin - strlen(items[i]) / 2,
                "%s", items[i]);
    
            if (highlight == i)
                wattroff(w, A_STANDOUT);
        }
    }
    
    int main_menu(int xm, int ym, int selected)
    {
        WINDOW *win = newwin(ym/2, xm/2, ym/4, xm/4);
        keypad(win, TRUE);
    
        while (1) {
            werase(win);
    
            box(win, 0, 0);
            mvwprintw(win, 0, xm/4 - 2, "Snake");
    
            print_list(win, menu_entries, MENU_LENGTH,
                    selected, ym / 4, xm / 4, 2);
    
            wrefresh(win);
    
            int input = wgetch(win);
    
            if (KEY_UP == input || 'w' == input) {
                if (selected != 0) selected--;
            }
    
            if (KEY_DOWN == input || 's' == input) {
                if (selected != 3) selected++;
            }
    
            if ('\n' == input || ' ' == input)
                break;
        }
    
        wclear(win);
        wrefresh(win);
        delwin(win);
    
        return selected;
    }
    
    void fake_loading(int xm, int ym)
    {
        WINDOW *win = newwin(ym/2, xm/2, ym/4, xm/4);
        int anim = 2;
        int intro = 3;
    
        while (1) {
            werase(win);
    
            box(win, 0, 0);
            mvwprintw(win, 0, anim, "Snake");
            mvwprintw(win, ym/4, xm/4 - 5, "Loading...");
    
            wrefresh(win);
    
            napms(30);
    
            if (intro == 3) {
                if (anim < xm/2 - 7) anim++;
                else intro--;
            }
    
            if (intro == 2) {
                if(anim > 2) anim--;
                else intro--;
            }
    
            if (intro == 1) {
                if(anim < xm/4 - 2) anim++;
                else intro--;
            }
    
            if (intro == 0) break;
        }
    
        wclear(win);
        wrefresh(win);
        delwin(win);
    }
    
    void rules(int xm, int ym)
    {
        WINDOW *win = newwin(3, 30, ym / 2 - 1, xm / 2 - 15);
    
        int wxm, wym;
        getmaxyx(win, wym, wxm);
    
        box(win, 0, 0);
        mvwprintw(win, 0, wxm / 2 - 6, "Snake - Rules");
        mvwprintw(win, 1, wxm / 2 - 5, "Eat stuff.");
    
        wrefresh(win);
    
        wgetch(win);
    
        wclear(win);
        wrefresh(win);
        delwin(win);
    }
    
    int main(void)
    {
        initscr();
        noecho();
        curs_set(0);
    
        int xm, ym;
        getmaxyx(stdscr, ym, xm);
    
        int running = 1;
        int last_sel = 0;
    
        fake_loading(xm, ym);
    
        while (running) {
            erase();
    
            last_sel = main_menu(xm, ym, last_sel);
    
            switch (last_sel) {
                case MENU_RULES:
                    rules(xm, ym);
                    break;
                case MENU_QUIT:
                    running = 0;
                    break;
            }
    
            refresh();
        }
    
        endwin();
    }