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