Drawing ncurses on remote terminals
This post describes how to have a server operate ncurses on its clients’ sockets.
Limitations
ncurses has to know what sort of terminal the client is using beforehand. In this post I will assume it’s xterm.
ncurses isn’t thread-safe. For handling multiple clients a multiplexed approach is required rather than pthreads.
Drawing to a remote terminal
After the standard way of socket creation, binding, listening and accepting a client socket is returned.
int one = 1, port = 2215, client_fd;
struct sockaddr_in serv_addr, cli_addr;
socklen_t sin_len = sizeof(cli_addr);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);
bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
listen(sockfd, 5);
clientfd = accept(sockfd, (struct sockaddr *) &cli_addr, &sin_len); // the client fd
The ncurses function newterm
allows ncurses to use different pointers than stdout and stdin. Instead of initscr
, on each connection newterm
must be called.
SCREEN *newterm(char *type, FILE *outfp, FILE *infp)
The function takes file pointers as arguments but accept
returns the client as a file descriptor (socket). It must be converted into a file pointer before it can be used with newterm
.
FILE *client_fp = fdopen(clientfd, "rw");
SCREEN *cliscr = newterm("xterm", client_fp, client_fp);
And finally ncurses will draw to it.
set_term(cliscr); // switch to remote terminal
WINDOW *win = newwin(20, 40, 1, 2);
box(win, 0, 0);
mvwprintw(win, 20/2, (40-18)/2, "Enter 'q' to quit.");
if (wgetch(win) == 'q') {
endwin();
fclose(client_fp);
}