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.
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();
}