如何使用ncurses在窗口中打印从命名管道读取的迷宫矩阵?
问题分析与修复方案
核心问题:窗口被提前销毁
代码中创建janelaTopo后,过早执行了delwin(janelaTopo);,直接销毁了顶部窗口。后续读取管道时再用这个无效指针操作窗口,属于非法内存访问,自然无法显示内容。
具体修复步骤
1. 调整窗口生命周期
将窗口销毁代码移到读取管道并显示迷宫之后,也就是endwin()之前。修改后main函数的核心结构如下:
int main(int argc, char *argv[]) { // ... 参数检查、ncurses初始化、窗口创建代码 ... writeMap(janelaTopo, 2); wprintw(janelaBaixo, "Linha de Comandos\n"); writeMap(janelaBaixo, 1); trataTeclado(janelaTopo, janelaBaixo); wrefresh(janelaTopo); // 此处不要删除窗口 // ... 管道读写代码 ... // 迷宫显示完成后再销毁窗口 delwin(janelaTopo); wclear(janelaBaixo); wrefresh(janelaBaixo); delwin(janelaBaixo); endwin(); return 0; }
2. 修正函数调用笔误
你定义的窗口绘制函数是writeMap,但代码中调用的是desenhaMapa,这会导致编译错误或异常行为,统一函数名:
// 替换调用处的desenhaMapa为writeMap writeMap(janelaTopo, 2); wprintw(janelaBaixo, "Linha de Comandos\n"); writeMap(janelaBaixo, 1);
3. 替换ncurses模式下的printf
ncurses初始化后使用printf会破坏终端虚拟屏幕布局,把超时提示换成ncurses原生打印函数:
// 替换原printf语句 mvprintw(0, 0, "Timeout - Não foram recebidos dados do servidor\n"); refresh();
4. 确保迷宫绘制在窗口内部
writeMap函数绘制了窗口边框,打印迷宫时需要避开边框,将光标定位到窗口内部起始位置:
// 读取管道的循环中,先移动光标到边框内的起始位置 int row = 1; // 从边框内第一行开始 while (read(fd_servidor, linhaRecebida, sizeof(linhaRecebida)) > 0) { wmove(janelaTopo, row++, 1); // 移动光标到当前行第1列 wprintw(janelaTopo, "%s", linhaRecebida); wrefresh(janelaTopo); }
5. 处理换行符问题
如果管道读取的每行末尾没有换行符,手动添加避免内容挤在一行:
wprintw(janelaTopo, "%s\n", linhaRecebida);
修复后关键代码片段
int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "USo: %s <Nome do Jogador>\n", argv[0]); exit(EXIT_FAILURE); } if (access("cliente_servidor", F_OK) != 0) { printf("[ERRO] O 'motor' nao esta a funcionar!\n"); exit(1); } initscr(); raw(); noecho(); keypad(stdscr, TRUE); attrset(A_DIM); WINDOW *janelaTopo = newwin(30, 82, 1, 1); WINDOW *janelaBaixo = newwin(15, 82, 33, 1); writeMap(janelaTopo, 2); wprintw(janelaBaixo, "Linha de Comandos\n"); writeMap(janelaBaixo, 1); trataTeclado(janelaTopo, janelaBaixo); wrefresh(janelaTopo); // 管道操作部分 int fd_cliente = open("cliente_servidor", O_WRONLY); char nome_jogador[TAMANHO_NOME]; strncpy(nome_jogador, argv[1], sizeof(nome_jogador)); if (write(fd_cliente, &nome_jogador, sizeof(nome_jogador)) == -1) { perror("Erro ao escrever no FIFO no JOGOUI"); return 1; } close(fd_cliente); int fd_servidor = open("servidor_cliente", O_RDONLY); if (fd_servidor == -1) { perror("Erro ao abrir o FIFO servidor_cliente no JOGOUI"); return 2; } fd_set set; struct timeval timeout; FD_ZERO(&set); FD_SET(fd_servidor, &set); timeout.tv_sec = 5; timeout.tv_usec = 0; int result = select(fd_servidor + 1, &set, NULL, NULL, &timeout); if (result == -1) { perror("Erro durante a chamada do select"); close(fd_servidor); return 3; } else if (result == 0) { mvprintw(0, 0, "Timeout - Não foram recebidos dados do servidor\n"); refresh(); } else { char linhaRecebida[NCOL]; int row = 1; while (read(fd_servidor, linhaRecebida, sizeof(linhaRecebida)) > 0) { wmove(janelaTopo, row++, 1); wprintw(janelaTopo, "%s", linhaRecebida); wrefresh(janelaTopo); } } close(fd_servidor); // 销毁窗口移至最后 delwin(janelaTopo); wclear(janelaBaixo); wrefresh(janelaBaixo); delwin(janelaBaixo); endwin(); return 0; }
内容的提问来源于stack exchange,提问作者João Simões
相关产品推荐
相关产品推荐

