blob: fa2cf7915ab8f913e6557f744ea8a76ae3c3569e [file] [log] [blame]
/* xterm.c */
/* This "driver" is designed to go on top of an existing driver
to provide support for features only present if using an
xterm or compatible program for your console output. It should
inlcude such features as resizing, separation of output from the
standard wine console, and a configurable title bar for
Win32 console. */
/* Right now, it doesn't have any "special" features */
#include <signal.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include "windows.h"
#include "console.h"
#include "debug.h"
#define ESC '\x1b'
static BOOL32 wine_create_console(FILE **master, FILE **slave, int *pid);
static FILE *wine_openpty(FILE **master, FILE **slave, char *name,
struct termios *term, struct winsize *winsize);
/* The console -- I chose to keep the master and slave
* (UNIX) file descriptors around in case they are needed for
* ioctls later. The pid is needed to destroy the xterm on close
*/
typedef struct _XTERM_CONSOLE {
FILE *master; /* xterm side of pty */
FILE *slave; /* wine side of pty */
int pid; /* xterm's pid, -1 if no xterm */
} XTERM_CONSOLE;
static XTERM_CONSOLE xterm_console;
CONSOLE_device chain;
FILE *old_in, *old_out;
void XTERM_Start()
{
/* Here, this is a supplementary driver so we should remember to call
the chain. */
chain.init = driver.init;
driver.init = XTERM_Init;
chain.close = driver.close;
driver.close = XTERM_Close;
chain.resizeScreen = driver.resizeScreen;
driver.resizeScreen = XTERM_ResizeScreen;
}
void XTERM_Init()
{
wine_create_console(&xterm_console.master, &xterm_console.slave,
&xterm_console.pid);
old_in = driver.console_in;
driver.console_in = xterm_console.slave;
old_out = driver.console_out;
driver.console_out = xterm_console.slave;
/* Then call the chain... */
if (chain.init)
chain.init();
}
void XTERM_Close()
{
/* Call the chain first... */
if (chain.close)
chain.close();
driver.console_in = old_in;
driver.console_out = old_out;
/* make sure a xterm exists to kill */
if (xterm_console.pid != -1) {
kill(xterm_console.pid, SIGTERM);
}
}
void XTERM_ResizeScreen(int x, int y)
{
char temp[100];
/* Call the chain first, there shoudln't be any... */
if (chain.resizeScreen)
chain.resizeScreen(x, y);
sprintf(temp, "\x1b[8;%d;%dt", y, x);
CONSOLE_WriteRawString(temp);
CONSOLE_NotifyResizeScreen(x, y);
}
/**
* It looks like the openpty that comes with glibc in RedHat 5.0
* is buggy (second call returns what looks like a dup of 0 and 1
* instead of a new pty), this is a generic replacement.
*/
/** Can't we determine this using autoconf?
*/
static FILE *wine_openpty(FILE **master, FILE **slave, char *name,
struct termios *term, struct winsize *winsize)
{
FILE *fdm, *fds;
char *ptr1, *ptr2;
char pts_name[512];
strcpy (pts_name, "/dev/ptyXY");
for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != 0; ptr1++) {
pts_name[8] = *ptr1;
for (ptr2 = "0123456789abcdef"; *ptr2 != 0; ptr2++) {
pts_name[9] = *ptr2;
if ((fdm = fopen(pts_name, "r+")) == NULL) {
if (errno == ENOENT)
return (FILE *) -1;
else
continue;
}
pts_name[5] = 't';
if ((fds = fopen(pts_name, "r+")) == NULL) {
pts_name[5] = 'p';
continue;
}
*master = fdm;
*slave = fds;
if (term != NULL)
tcsetattr(fileno(*slave), TCSANOW, term);
if (winsize != NULL)
ioctl(fileno(*slave), TIOCSWINSZ, winsize);
if (name != NULL)
strcpy(name, pts_name);
return fds;
}
}
return (FILE *) -1;
}
static BOOL32 wine_create_console(FILE **master, FILE **slave, int *pid)
{
struct termios term;
char buf[1024];
char c = '\0';
int status = 0;
int i;
if (tcgetattr(0, &term) < 0) return FALSE;
term.c_lflag |= ICANON;
term.c_lflag &= ~ECHO;
if (wine_openpty(master, slave, NULL, &term, NULL) < 0)
return FALSE;
if ((*pid=fork()) == 0) {
tcsetattr(fileno(*slave), TCSADRAIN, &term);
sprintf(buf, "-Sxx%d", fileno(*master));
execlp(CONSOLE_XTERM_PROG, CONSOLE_XTERM_PROG, buf, "-fg",
"white", "-bg", "black", NULL);
ERR(console, "error creating xterm\n");
exit(1);
}
/* most xterms like to print their window ID when used with -S;
* read it and continue before the user has a chance...
* NOTE: this is the reason we started xterm with ECHO off,
* we'll turn it back on below
*/
for (i=0; c!='\n'; (status=fread(&c, 1, 1, *slave)), i++) {
if (status == -1 && c == '\0') {
/* wait for xterm to be created */
usleep(100);
}
if (i > 10000) {
WARN(console, "can't read xterm WID\n");
kill(*pid, SIGKILL);
return FALSE;
}
}
term.c_lflag |= ECHO;
tcsetattr(fileno(*master), TCSADRAIN, &term);
return TRUE;
}