mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-03 17:24:18 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			332 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* $Id: window.c,v 1.19 2007-10-04 20:33:16 nicm Exp $ */
 | 
						|
 | 
						|
/*
 | 
						|
 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
 | 
						|
 *
 | 
						|
 * Permission to use, copy, modify, and distribute this software for any
 | 
						|
 * purpose with or without fee is hereby granted, provided that the above
 | 
						|
 * copyright notice and this permission notice appear in all copies.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
						|
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
						|
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
						|
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 | 
						|
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 | 
						|
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/ioctl.h>
 | 
						|
 | 
						|
#include <fcntl.h>
 | 
						|
#include <paths.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <termios.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <util.h>
 | 
						|
 | 
						|
#include "tmux.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * Each window is attached to a pty. This file contains code to handle them.
 | 
						|
 *
 | 
						|
 * A window has two buffers attached, these are filled and emptied by the main
 | 
						|
 * server poll loop. Output data is received from pty's in screen format,
 | 
						|
 * translated and returned as a series of escape sequences and strings.
 | 
						|
 * Input data is received in screen format and written directly to the pty
 | 
						|
 * (translation is done in the client).
 | 
						|
 *
 | 
						|
 * Each window also has a "virtual" screen (screen.c) which contains the
 | 
						|
 * current state and is redisplayed when the window is reattached to a client.
 | 
						|
 *
 | 
						|
 * A global list of windows is maintained, and a window may also be a member
 | 
						|
 * of any number of sessions. A reference count is maintained and a window
 | 
						|
 * removed from the global list and destroyed when it reaches zero.
 | 
						|
 */
 | 
						|
 | 
						|
/* Global window list. */
 | 
						|
struct windows	windows;
 | 
						|
 | 
						|
/* Create a new window. */
 | 
						|
struct window *
 | 
						|
window_create(
 | 
						|
    const char *name, const char *cmd, const char **environ, u_int sx, u_int sy)
 | 
						|
{
 | 
						|
	struct window	*w;
 | 
						|
	struct winsize	 ws;
 | 
						|
	struct termios	 tio;
 | 
						|
	int		 fd, mode;
 | 
						|
	char		*ptr, *copy;
 | 
						|
	const char     **entry;
 | 
						|
 | 
						|
	memset(&ws, 0, sizeof ws);
 | 
						|
	ws.ws_col = sx;
 | 
						|
	ws.ws_row = sy;
 | 
						|
 | 
						|
	memset(&tio, 0, sizeof tio);
 | 
						|
	tio.c_iflag = TTYDEF_IFLAG;
 | 
						|
	tio.c_oflag = TTYDEF_OFLAG;
 | 
						|
	tio.c_lflag = TTYDEF_LFLAG;
 | 
						|
	tio.c_cflag = TTYDEF_CFLAG;
 | 
						|
	memcpy(&tio.c_cc, ttydefchars, sizeof tio.c_cc);
 | 
						|
	cfsetspeed(&tio, TTYDEF_SPEED);
 | 
						|
 | 
						|
	switch (forkpty(&fd, NULL, &tio, &ws)) {
 | 
						|
	case -1:
 | 
						|
		return (NULL);
 | 
						|
	case 0:
 | 
						|
		for (entry = environ; *entry != NULL; entry++) {
 | 
						|
			if (putenv(*entry) != 0)
 | 
						|
				fatal("putenv failed");
 | 
						|
		}
 | 
						|
		sigreset();
 | 
						|
		log_close();
 | 
						|
 | 
						|
		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
 | 
						|
		fatal("execl failed");
 | 
						|
	}
 | 
						|
 | 
						|
	if ((mode = fcntl(fd, F_GETFL)) == -1)
 | 
						|
		fatal("fcntl failed");
 | 
						|
	if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
 | 
						|
		fatal("fcntl failed");
 | 
						|
 | 
						|
	mode = 1;
 | 
						|
	if (ioctl(fd, TIOCPKT, &mode) == -1)
 | 
						|
		fatal("ioctl failed");
 | 
						|
 | 
						|
	w = xmalloc(sizeof *w);
 | 
						|
	w->fd = fd;
 | 
						|
	w->in = buffer_create(BUFSIZ);
 | 
						|
	w->out = buffer_create(BUFSIZ);
 | 
						|
	screen_create(&w->screen, sx, sy);
 | 
						|
	input_init(&w->ictx, &w->screen);
 | 
						|
 | 
						|
	if (name == NULL) {
 | 
						|
		/* XXX */
 | 
						|
		if (strncmp(cmd, "exec ", (sizeof "exec ") - 1) == 0)
 | 
						|
			copy = xstrdup(cmd + (sizeof "exec ") - 1);
 | 
						|
		else
 | 
						|
			copy = xstrdup(cmd);
 | 
						|
		if ((ptr = strchr(copy, ' ')) != NULL) {
 | 
						|
			if (ptr != copy && ptr[-1] != '\\')
 | 
						|
				*ptr = '\0';
 | 
						|
			else {
 | 
						|
				while ((ptr = strchr(ptr + 1, ' ')) != NULL) {
 | 
						|
					if (ptr[-1] != '\\') {
 | 
						|
						*ptr = '\0';
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		w->name = xstrdup(xbasename(copy));
 | 
						|
		xfree(copy);
 | 
						|
	} else
 | 
						|
		w->name = xstrdup(name);
 | 
						|
 | 
						|
	window_add(&windows, w);
 | 
						|
	w->references = 1;
 | 
						|
 | 
						|
	return (w);
 | 
						|
}
 | 
						|
 | 
						|
/* Find window index in list. */
 | 
						|
int
 | 
						|
window_index(struct windows *ww, struct window *w, u_int *i)
 | 
						|
{
 | 
						|
	for (*i = 0; *i < ARRAY_LENGTH(ww); (*i)++) {
 | 
						|
		if (w == ARRAY_ITEM(ww, *i))
 | 
						|
			return (0);
 | 
						|
	}
 | 
						|
	return (-1);
 | 
						|
}
 | 
						|
	
 | 
						|
/* Add a window to a list. */
 | 
						|
void
 | 
						|
window_add(struct windows *ww, struct window *w)
 | 
						|
{
 | 
						|
	u_int	i;
 | 
						|
 | 
						|
	if (window_index(ww, NULL, &i) != 0)
 | 
						|
		ARRAY_ADD(ww, w);
 | 
						|
	else
 | 
						|
		ARRAY_SET(ww, i, w);
 | 
						|
 | 
						|
	w->references++;
 | 
						|
}
 | 
						|
 | 
						|
/* Remove a window from a list. */
 | 
						|
void
 | 
						|
window_remove(struct windows *ww, struct window *w)
 | 
						|
{
 | 
						|
	u_int	i;
 | 
						|
 | 
						|
	if (window_index(ww, w, &i) != 0)
 | 
						|
		fatalx("window not found");
 | 
						|
	ARRAY_SET(ww, i, NULL);
 | 
						|
	while (!ARRAY_EMPTY(ww) && ARRAY_LAST(ww) == NULL)
 | 
						|
		ARRAY_TRUNC(ww, 1);
 | 
						|
 | 
						|
	w->references--;
 | 
						|
	if (w->references == 0)
 | 
						|
		window_destroy(w);
 | 
						|
	if (w->references == 1)
 | 
						|
		window_remove(&windows, w);
 | 
						|
}
 | 
						|
 | 
						|
/* Destroy a window. */
 | 
						|
void
 | 
						|
window_destroy(struct window *w)
 | 
						|
{
 | 
						|
	close(w->fd);
 | 
						|
 | 
						|
	input_free(&w->ictx);
 | 
						|
 | 
						|
	screen_destroy(&w->screen);
 | 
						|
 | 
						|
	buffer_destroy(w->in);
 | 
						|
	buffer_destroy(w->out);
 | 
						|
 | 
						|
	xfree(w->name);
 | 
						|
	xfree(w);
 | 
						|
}
 | 
						|
 | 
						|
/* Locate next window in list. */
 | 
						|
struct window *
 | 
						|
window_next(struct windows *ww, struct window *w)
 | 
						|
{
 | 
						|
	u_int	i;
 | 
						|
 | 
						|
	if (window_index(ww, w, &i) != 0)
 | 
						|
		fatalx("window not found");
 | 
						|
 | 
						|
	if (i == ARRAY_LENGTH(ww) - 1)
 | 
						|
		return (NULL);
 | 
						|
	do {
 | 
						|
		i++;
 | 
						|
		w = window_at(ww, i);
 | 
						|
		if (w != NULL)
 | 
						|
			return (w);
 | 
						|
	} while (i != ARRAY_LENGTH(ww) - 1);
 | 
						|
	return (NULL);
 | 
						|
} 
 | 
						|
 | 
						|
/* Locate previous window in list. */
 | 
						|
struct window *
 | 
						|
window_previous(struct windows *ww, struct window *w)
 | 
						|
{
 | 
						|
	u_int	i;
 | 
						|
 | 
						|
	if (window_index(ww, w, &i) != 0)
 | 
						|
		fatalx("window not found");
 | 
						|
	if (i == 0)
 | 
						|
		return (NULL);
 | 
						|
	do {
 | 
						|
		i--;
 | 
						|
		w = window_at(ww, i);
 | 
						|
		if (w != NULL)
 | 
						|
			return (w);
 | 
						|
	} while (i != 0);
 | 
						|
	return (NULL);
 | 
						|
}
 | 
						|
 | 
						|
/* Locate window at specific position in list. */
 | 
						|
struct window *
 | 
						|
window_at(struct windows *ww, u_int i)
 | 
						|
{
 | 
						|
	if (i >= ARRAY_LENGTH(ww))
 | 
						|
		return (NULL);
 | 
						|
	return (ARRAY_ITEM(ww, i));
 | 
						|
} 
 | 
						|
 | 
						|
/* Resize a window. */
 | 
						|
int
 | 
						|
window_resize(struct window *w, u_int sx, u_int sy)
 | 
						|
{
 | 
						|
	struct winsize	ws;
 | 
						|
 | 
						|
	if (sx == w->screen.sx && sy == w->screen.sy)
 | 
						|
		return (-1);
 | 
						|
		
 | 
						|
	memset(&ws, 0, sizeof ws);
 | 
						|
	ws.ws_col = sx;
 | 
						|
	ws.ws_row = sy;
 | 
						|
 | 
						|
	screen_resize(&w->screen, sx, sy);
 | 
						|
 | 
						|
	if (ioctl(w->fd, TIOCSWINSZ, &ws) == -1)
 | 
						|
		fatal("ioctl failed");
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
/* Handle window poll results. This is special because of TIOCPKT. */
 | 
						|
int
 | 
						|
window_poll(struct window *w, struct pollfd *pfd)
 | 
						|
{
 | 
						|
	struct termios	 tio;
 | 
						|
	size_t	 	 size;
 | 
						|
	u_char		*ptr;
 | 
						|
 | 
						|
	size = BUFFER_USED(w->in);
 | 
						|
	if (buffer_poll(pfd, w->in, w->out) != 0)
 | 
						|
		return (-1);
 | 
						|
 | 
						|
	if (BUFFER_USED(w->in) == size)
 | 
						|
		return (0);
 | 
						|
	ptr = BUFFER_IN(w->in) - (BUFFER_USED(w->in) - size);
 | 
						|
 | 
						|
	log_debug("window packet: %hhu", *ptr);
 | 
						|
	switch (*ptr) {
 | 
						|
	case TIOCPKT_DATA:
 | 
						|
	case TIOCPKT_FLUSHREAD:
 | 
						|
	case TIOCPKT_FLUSHWRITE:
 | 
						|
	case TIOCPKT_STOP:
 | 
						|
	case TIOCPKT_START:
 | 
						|
	case TIOCPKT_DOSTOP:
 | 
						|
	case TIOCPKT_NOSTOP:
 | 
						|
		buffer_delete_range(w->in, size, 1);
 | 
						|
		break;
 | 
						|
	case TIOCPKT_IOCTL:
 | 
						|
		buffer_delete_range(w->in, size, 1 + sizeof tio);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
/* Process window key. */
 | 
						|
void
 | 
						|
window_key(struct window *w, int key)
 | 
						|
{
 | 
						|
	input_translate_key(w->out, key);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Process window output. Output is translated into a series of escape
 | 
						|
 * sequences and strings and returned.
 | 
						|
 */
 | 
						|
void
 | 
						|
window_data(struct window *w, struct buffer *b)
 | 
						|
{
 | 
						|
	FILE	*f;
 | 
						|
 | 
						|
	if (BUFFER_USED(w->in) == 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (debug_level > 2) {
 | 
						|
		f = fopen("tmux-in.log", "a+");
 | 
						|
		fwrite(BUFFER_OUT(w->in), BUFFER_USED(w->in), 1, f);
 | 
						|
		fclose(f);
 | 
						|
	}
 | 
						|
 | 
						|
	input_parse(&w->ictx, BUFFER_OUT(w->in), BUFFER_USED(w->in), b);
 | 
						|
	buffer_remove(w->in, BUFFER_USED(w->in));
 | 
						|
 | 
						|
	if (INPUT_FLAGS(&w->ictx) & INPUT_BELL)
 | 
						|
		w->flags |= WINDOW_BELL;
 | 
						|
}
 |