/* * Curses demo for CS435 * Feel free to forget / not use all this stuff if you don't like it * But, if you like writing stuff for the terminal, this is probably useful * This is a valid, if excessive, solution to lab 3. * * A couple points that might not be obvious: * - It's important to call endwin() after using curses, or the terminal might get all messed * up. That's why SIGINT has to be handled. * - The network stuff can fail before the display is even initiated. Also, when we endwin(), * there's no information left on the screen. So we use perror to display errors, so that * the user can figure out what they did wrong and try again. Same for the usage info. * - Newlines are added, because the user pressed enter (they're not in the output from * wgetstr normally). * - Newlines are added to incoming info. If this was going to be a program people actually * used, this should be an option. */ #include #include #include #include #include #include #include #include #include #include // For initial printf, later perror #include #include struct receive_info { int sockfd; WINDOW* top; WINDOW* bottom; }; void exit_graceful(int signal){ endwin(); exit(0); } void* receive_print(void* arg){ struct receive_info* prfi = (struct receive_info*)arg; char readstring[1024*1024]; ssize_t readsize; for(;;){ readsize = read(prfi->sockfd, readstring, 1024*1024); /* * If we read nothing at all, that probably means the connection was terminated. It's * certainly possible an internal OS error could cause read to stop blocking despite an * active connection, but that's not our fault, and system errors are expected to cause * programs to act strange. */ if(!readsize){ wattron(prfi->top, COLOR_PAIR(2)); wprintw(prfi->top, "Connection Terminated\n"); wattroff(prfi->top, COLOR_PAIR(2)); wmove(prfi->bottom, LINES/4 - 2, 0); wrefresh(prfi->top); wrefresh(prfi->bottom); break; } readstring[readsize] = 0; wprintw(prfi->top, readstring); wprintw(prfi->top, "\n"); wmove(prfi->bottom, LINES/4 - 2, 0); wrefresh(prfi->top); wrefresh(prfi->bottom); } return 0; } int main(int argc, char ** argv){ // Usage Information if(argc < 3){ printf("Usage: %s hostname port\n", argv[0]); return 1; } // Prepare the network connection, but don't call connect yet int sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd == -1) goto err; short port = atoi(argv[2]); struct sockaddr_in connect_addr; connect_addr.sin_port = htons(port); connect_addr.sin_family = AF_INET; struct hostent* entry = gethostbyname(argv[1]); if(!entry) goto err; struct in_addr **addr_list = (struct in_addr**)entry->h_addr_list; struct in_addr* c_addr = addr_list[0]; char* ip_string = inet_ntoa(*c_addr); connect_addr.sin_addr = *c_addr; // Set up curses. This is probably easier in Python. initscr(); start_color(); use_default_colors(); init_pair(1, COLOR_GREEN, -1); init_pair(2, COLOR_RED, -1); refresh(); WINDOW* top = newwin(LINES*3/4, COLS, 0, 0); WINDOW* bottom = newwin(LINES/4 - 1, COLS, LINES*3/4 + 1, 0); refresh(); wmove(stdscr, LINES*3/4, 0); whline(stdscr, ACS_HLINE , COLS); wmove(bottom, LINES/4 - 2, 0); scrollok(bottom, 1); scrollok(top, 1); wrefresh(top); wrefresh(bottom); refresh(); // Handle Signals struct sigaction sa; sa.sa_handler = exit_graceful; sigaction(SIGINT, &sa, 0); // The UI is up, let's reassure the user that whatever name they typed resolved to something wattron(top, COLOR_PAIR(1)); wprintw(top, "Connecting to host %s (%s)\n", entry->h_name, ip_string); wrefresh(top); // Actually connect. It might connect right away, or sit here and hang - depends on how the // host is feeling today if(connect(sockfd, (struct sockaddr*)&connect_addr, sizeof(struct sockaddr_in))) goto err; // Let the user know we're connected, so they can start doing whatever they do. wprintw(top, "Connected\n"); wrefresh(top); wattroff(top, COLOR_PAIR(1)); // Start the receive thread struct receive_info rfi; rfi.sockfd = sockfd; rfi.top = top; rfi.bottom = bottom; pthread_t t; pthread_create(&t, 0, receive_print, &rfi); // Get user input. Ctrl + C is the way out now. char input[1024*1024]; wmove(bottom, LINES/4 - 2, 0); wgetnstr(bottom, input, 1024*1024-1); size_t length; for(;;){ length = strlen(input); if(!strcmp(input, "exit")) exit_graceful(0); input[length] = '\n'; input[length+1] = 0; write(sockfd, input, length+1); wscrl(bottom, 0); wrefresh(bottom); wgetnstr(bottom, input, 1024*1024-1); wrefresh(bottom); } err: endwin(); perror(argv[0]); return 1; }